您的位置:首页 > 其它

6大设计原则之2--里氏替换原则

2015-12-31 00:00 295 查看
面向对象语言中,继承是必不可少的、非常优秀的语言机制,它有如下优点:

代码共享,减少创建类的工作量,每个子类都拥有父类的方法和属性;

提高代码的重用性

子类可以形似父类,但又异于父类。

提高代码的可扩展性,实现父类的方法就可以“为所欲为”了。

提高产品或项目的开放性。

自然界是充满哲学的,又有点必有缺点。继承的缺点如下:

继承是侵入性的。只要继承,就必须拥有父类的所有属性和方法;

降低代码的灵活性。子类必须拥有父类的属性和方法,让子类的自由世界里多了些约束

增强了耦合性。当父类的常量、变量和方法被修改时,需要考虑子类的修改,而且在缺乏规范的环境下,这种修改可能带来非常糟糕的结果--大段代码需要重构。

什么是里氏替换原则呢?它有两种定义:


第一种定义,也是最正宗的定义:If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T,the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T.(如果对每一个类型为S的对象o1,都有类型为T的对 象o2,使得以T定义的所有程序P在所有的对象o1都代换成o2时,程序P的行为没有发生变 化,那么类型S是类型T的子类型。)

第二种定义:Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.(所有引用基类的地方必须能透明地使用其子类的 对象。)

第二个定义是最清晰明确的,通俗点讲,只要父类能出现的地方子类就可以出现,而且 替换为子类也不会产生任何错误或异常,使用者可能根本就不需要知道是父类还是子类。但 是,反过来就不行了,有子类出现的地方,父类未必就能适应。

里氏替换原则为良好的继承定义了一个规范,一句简单的定义包含了4层含义。

子类必须完全实现父类的方法:注意 在类中调用其他类时务必要使用父类或接口,如果不能使用父类或接口,则说明 类的设计已经违背了LSP原则。

子类可以有自己的个性

覆盖或实现父类的方法时输入参数可以被放大:(方法中的输入参数称为前置条件,这是什么意思呢?大家做过Web Service开发就应该知 道有一个“契约优先”的原则,也就是先定义出WSDL接口,制定好双方的开发协议,然后再 各自实现。里氏替换原则也要求制定一个契约,就是父类或接口,这种设计方法也叫做 Design by Contract(契约设计),与里氏替换原则有着异曲同工之妙。契约制定了,也就同 时制定了前置条件和后置条件,前置条件就是你--父类要让我--子类执行,就必须满足我的条件;后置条 件就是我执行完了需要反馈,标准是什么。)

覆写或实现父类的方法时输出结果可以被缩小:(这是什么意思呢,父类的一个方法的返回值是一个类型T,子类的相同方法(重载或覆 写)的返回值为S,那么里氏替换原则就要求S必须小于等于T,也就是说,要么S和T是同一 个类型,要么S是T的子类,为什么呢?分两种情况,如果是覆写,父类和子类的同名方法的 输入参数是相同的,两个方法的范围值S小于等于T,这是覆写的要求,这才是重中之重,子 类覆写父类的方法,天经地义。如果是重载,则要求方法的输入参数类型或数量不相同,在 里氏替换原则要求下,就是子类的输入参数宽于或等于父类的输入参数,也就是说你写的这 个方法是不会被调用的,参考上面讲的前置条件。)

采用里氏替换原则的目的就是增强程序的健壮性,版本升级时也可以保持非常好的兼容 性。即使增加子类,原有的子类还可以继续运行。在实际项目中,每个子类对应不同的业务 含义,使用父类作为参数,传递不同的子类完成不同的业务逻辑,非常完美!

在项目中,采用里氏替换原则时,尽量避免子类的“个性”,一旦子类有“个性”,这个子 类和父类之间的关系就很难调和了,把子类当做父类使用,子类的“个性”被抹杀——委屈了 点;把子类单独作为一个业务来使用,则会让代码间的耦合关系变得扑朔迷离——缺乏类替 换的标准。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: