设计模式to be androider

【六大设计原则】里氏替换原则

2018-06-09  本文已影响12人  杨志聪

面向对象语言都有继承的特性,里氏替换原则要求,任何基类出现的地方,都可以将该基类替换为其子类,意思就是说无论在任何地方,子类都可以接替父类来使用。

下面举个符合里氏替换原则的例子。我们声明一个Bird父类,它只有一个方法fly,然后我们继承Bird生成两个子类,分别是Swallow(燕子)和Eagle(老鹰):

Bird父类:

@interface Bird : NSObject
- (void)fly;
@end

@implementation Bird
- (void)fly{
   //Bird为抽象类,fly方法由子类实现
}
@end

Swallow类:

@interface Swallow : Bird
@end

@implementation Swallow
- (void)fly{
    NSLog(@"燕子飞");
}
@end

Eagle类:

@interface Eagle : Bird
@end

@implementation Swallow
- (void)fly{
    NSLog(@"老鹰飞");
}
@end

接着就可以让我们的鸟飞一飞了:

Bird *aBird = [Bird new];
[aBird fly];

根据里氏替换原则:任何基类出现的地方,都可以将该基类替换为其子类,以上场景也可以替换为:

Bird *aBird = [Swallow new];
[aBird fly];

也可以替换为:

Bird *aBird = [Eagle new];
[aBird fly];

无论是将Bird替换为Swallow还是Eagle,完全没有问题。这就是里氏替换原则,非常简单,所以就算你之前没有听说过这个原则,相信你大部分的代码都是遵守这个原则的。

不过如果你没有意识去坚持这个原则,就可能会犯错误。下面举个不符合里氏替换原则的例子。比方说我们现在需要设计一个Chicken(鸡)类,鸡能不能算是一种鸟呢?鸡和鸟其实有很多类似之处的,例如:浑身都是羽毛,有爪子,能下蛋等,假设我们上面的Bird除了fly方法之外,也包含了这些属性。为了方便,我们就让Chicken类继承Bird。因为鸡不会飞,所以我们不实现fly方法:

@interface Chicken : Bird
//浑身都是羽毛,有爪子,能下蛋。。。
@end

@implementation Chicken
//实现除了“fly”之外的其他方法
@end

根据里氏替换原则,我们是可以让我们的鸡也飞一下的:

Bird *aBird = [Chicken new];
[aBird fly];

Chicken没有实现fly方法,所以该代码什么也不会发生。这好像也没什么问题,但是如果上述场景是把创建的鸟从十层楼上扔下去让它飞呢?想想我们可怜的的鸡的下场吧。所以Chicken在这里不能替换Bird,也就是不符合里氏替换原则。

总结

面向对象语言的继承特性,类似于现实生活中的继承,但又有很大区别。在现实生活中,子类并不是完全继承父类的属性,以父子关系为例,其实儿子很多特性并没有继承父亲的,要不很多父亲也不会对儿子抱怨“你有我的一半聪明就好了”。但是对于面向对象语言的继承,里氏替换原则要求子类必须完全实现父类的方法,否则就可能出现上述将鸡活活摔死的问题。

此外,对于支持方法重载的语言(例如JAVA),如果在子类中重载了父类的方法,那么里氏替换原则要求:

  1. 重载方法的输入参数,必须与父类中方法的输入参数相同或者更宽松;
  2. 重载方法的输出结果,必须与父类中方法的输出结果相同或者更狭窄。

由于Objective-C并不支持方法重载,对于上述两个要求就不细说了。

继承是面向对象语言中非常优秀的语言机制,里氏替换原则规范了继承的用法,避免使用继承可能出现的问题。

上一篇 下一篇

猜你喜欢

热点阅读