设计模式六大原则之--里氏替代原则(LSP)
1.里氏替代原则,(Liskov Substitution Principle,LSP ) 定义:Functions that use pointers or referrnces to base classes must be able to use objects of derived classes without knowing it.(所有引用基类的地方必须能透明地使用其子类的对象。)
2.理解:只要父类能出现的地方,子类就可以出现,并且替换为子类也不会产生任何错误或异常,使用者可能根本就不需要知道是父类还是子类。但反之,未要求。 继承机制的优点:
继承机制的缺点:
定义所包含的四层意思:(另一种通俗的LSP原则讲解:子类可以扩展父类的功能,但不能改变父类原有的功能)
Liskov替换原则并不是要求子类不能新增父类没有的方法或者属性。因为从调用父类的客户程序的角度来说,它关心的仅仅是父类的行为,只要子类对于父类的行为是可替换的,就不算是违背该原则。 恰恰相反,当你发现父类拥有子类不希望继承,或者勉强继承会对子类造成破坏时 ,正可以说明这个继承体系可能存在问题,违背了Liskov替换原则。这就充分说明,子类并不关心父类的行为,但却需要遵循父类制定的规范或契约,以满足客户调用父类的期望。正所谓"萧规曹随",如果前人制定的规范我们不遵循,反而要去打破,那就不是继承,而是铁了心要另起炉灶了。 这样的例子在谈对象设计的原则时,已经啰嗦得够多,这里我就不再赘述了 。 这个例子带来的教训就是,现实世界中继承的例子,不能够完全直接套用在程序世界中。 不过,作为设计的参照物,现实世界的很多规律与法则,我们仍然不可忽视。 例如鲸鱼和鱼,应该属于什么关系?从生物学的角度看,鲸鱼应该属于哺乳动物,而不是鱼类。
3.问题由来 有一功能P1,由类A完成。现需要将功能P1进行扩展,扩展后的功能为P,其中P由原有功能P1与新功能P2组成。新功能P由类A的子类B来完成,则子类B在完成新功能P2的同时,有可能会导原有功能P1发生故障。解决方案:遵循LSP:类B在继承类A时,除添加新的方法完成新增功能P2外,尽量不要重写父类A的方法,也尽量不要重载父类的A的方法。
4.好处 即为正确使用继承的好处。
5.难点 5.1 如何根据类的继承原则,确定是要继承当前类结构,还是要另起炉赵。 5.2 如何根据情况,在违背Liskov替换原则时,提出一种方案。
6.实践建议 6.1 在类中调用其它类时务必要使用父类或接口,如果不能使用父类或接口,则说明类的设计已违背了LSP;(例:Interface in = new Instance();); 6.2 如果子类不能完整地实现父类的方法,或者父类的某些方法在子类中已经发生“畸变”,则建议断开父子继承关系,采用依赖、聚集、组合等关系代替继承。 6.3 如果你的程序中出现了if/else之类对子类类型进行判断的条件,则说明类的设计已违背了LSP。
7.范例 7.1 矩形与正方形(现实中的继承,却不能直接用于程序中) 对于长方形的类,如果它的长宽相等,那么它就是一个正方形,因此,长方形类的对象中有一些正方形的对象。对于一个正方形的类,它的方法有个setSide和getSide,它不是长方形的子类,和长方形也不会符合LSP。 //长方形类: public class Rectangle{ ... setWidth(int width){ this.width=width; } setHeight(int height){ this.height=height } } //正方形类: public class Square{ ... setWidth(int width){ this.width=width; this. height=width; } setHeight(int height){ this.setWidth(height); } } //例子中改变边长的方法: public void resize(Rectangle r){ while(r.getHeight()<=r.getWidth){ r.setHeight(r.getWidth+1); } }那么,如果让正方形当做是长方形的子类,会出现什么情况呢?我们让正方形从长方形继承,然后在它的内部设置width等于height,这样,只要width或者height被赋值,那么width和height会被同时赋值,这样就保证了正方形类中,width和height总是相等的.现在我们假设有个客户类,其中有个方法,规则是这样的,测试传入的长方形的宽度是否大于高度,如果满足就停止下来,否则就增加宽度的值。现在我们来看,如果传入的是基类长方形,这个运行的很好。根据LSP,我们把基类替换成它的子类,结果应该也是一样的,但是因为正方形类的width和height会同时赋值,这个方法没有结束的时候,条件总是不满足,也就是说,替换成子类后,程序的行为发生了变化,它不满足LSP。 7.2 鲸鱼与鱼(避开违背LSP的一种解决方案) 鲸鱼和鱼,应该属于什么关系?从生物学的角度看,鲸鱼应该属于哺乳动物,而不是鱼类。没错,在程序世界中我们可以得出同样的结论。如果让鲸鱼类去继承鱼类,就完全违背了Liskov替换原则。因为鱼作为父类,很多特性是鲸鱼所不具备的,例如通过腮呼吸,以及卵生繁殖。那么,二者是否具有共性呢?有,那就是它们都可以在水中"游泳",从程序设计的角度来说,它们都共同实现了一个支持"游泳"行为的接口。
7.3 继承的风险 举例说明继承的风险,我们需要完成一个两数相减的功能,由类A来负责: class A{ public int func1(int a,int b){ return a-b; } } public class Client{ public static void main(String[] args){ A a = new A(); System.out.println("100-50="+a.func1(100,50)); System.out.println("100-80="+a.func1(100,80)); } } 运行结果: 100-50=50 100-80=20后来,我们需要增加一个新的功能:完成两数相加,然后再与100求和,由类B来负责。即类B需要完成两个功能:
class B extends A{ public int func1(int a,int b){ return a+b; } public int func2(int a,int b){ return func1(a,b)+100; } } public class Client{ public static void main(String[] args){ B a = new B(); System.out.println("100-50="+b.func1(100,50)); System.out.println("100-80="+b.func1(100,80)); System.out.println("100+20+100="+b.func2(100,20)); } } 运行结果: 100-50=150 100-80=180 100+20+100=220
转自:http://www.52php.cn/article/p-zlymbnnc-re.html (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |