【设计模式学习笔记】1:认识六大设计原则(OCP,LSV,DIP,ISP,LKP,SRP)
[1]开闭原则(Open Close Principle)
简述
对扩展是开放的,对修改是关闭的。即软件应当通过扩展来实现变化,而不是通过修改现有的代码。
理解
Java中的继承extends
关键字本意就是扩展,通过继承原有类来扩展功能对现有代码的影响是最小的,即在子类上加功能。但是GOF四人帮所提倡的设计模式思想之一就是优先使用组合而不是继承,所以这个还是比较复杂的一个问题。
优势
使用开闭原则可以提高系统的复用性和可维护性,能很好的适应软件在生命周期中的变化。
[2]里氏替换原则(Liskov Substitution Principle)
简述
任何基类可以出现的地方,都可以替换成子类,并使得软件的功能不受影响,这时基类才被真正复用。即所有引用基类的地方必须能透明地使用其子类的对象。
也就是说,里氏替换原则给出了判断是否可以用继承关系的一个依据,可以用来测试自己的父子类是否合乎规范。用子类替换掉父类,子类的成员不被使用,而是仍然使用父类的成员,这个成员可以是属性也可以是方法,这样才会使得软件的功能不受影响。
理解
我自己对里氏替换原则的理解是,这其实是一个语言设计者和使用者共同维护遵守的原则,使之在逻辑上真的具有继承的关系。
子类必须完全实现父类的方法,这一情形是编程语言的设计者来保证的。Java中当子类继承抽象父类时,会被强制要求覆写其抽象方法,并自动继承下来其非抽象的成员方法。但因此,继承这一OOP的特性是有诸多缺点的。因为继承使父类的方法、属性侵入了子类,子类必须拥有,所以子类就受到了父类的约束,当父类这些成员发生改变时,也会影响到子类。
所以对于编程语言的使用者而言,对这一情况没有选择,为了遵循这一逻辑,就应当要求子类逻辑上确实需要具备父类的所有成员,不然就不要设计成继承关系,而是组合关系等。
使用
我认为对编程语言设计者而言,其实就只要保证一点:
- 子类相对父类可以具有自己的成员,而不是相反
对编程语言使用者而言,为遵循里氏替换原则,主要应关注两方面:
- 子类Override的方法参数应当设计成放大的,这样才能保证LSP,见这里的例子
- 子类Override的方法返回值应当设计成缩小的
- 子类可以实现父类的抽象方法,但是不能覆盖父类的非抽象方法,这样替换后才能调用到
这里”缩小”和”放大”即指参数所在类的”细化”和”泛化”,也就是参数类的继承关系。
[3]依赖倒置原则(Dependence Inversion Principle)
简述
高层组件不应当依赖于底层组件,它们都应当依赖于抽象,也就是面向接口编程(Object Oriented Design)。
理解
高层组件时常会去使用低层的组件,如果直接将其组合进高层组件的属性中,或者在方法中直接创建低层组件的对象,或者将低层组件对象作为参数传入高层组件的方法中,都属于高层组件依赖了低层组件。这时当低层组件发生变化时,高层组件需要很大的改动,DIP正是解决了这样的问题,可以用抽象接口来实现模块之间的松耦合。
使用
主要注意以下几点:
- 程序中类的类型尽量是接口或者抽象类,在注入时注入其实现类来使用
- 尽量不从具体的类派生,而是继承抽象类或者实现接口。这就要求在合适的时机建立具体类
优势
关注DIP原则,并结合LSP原则,能较好的实现OCP原则。
[4]接口隔离原则(Interface Segregation Principle)
简述
保持接口细化、接口纯洁,也就是让接口中的方法尽可能少而精,保证各个接口抽象出了不同类型的功能,接口内不要混杂不甚相关的内容。
理解
因为DIP的面向接口编程,细化接口也就使得系统的功能之间耦合性得意降低,而纯洁的接口更是能让系统内模块的内聚性提高。
使用
接口越小,也就越细致,但同时也会让系统变得越发复杂,所以ISP原则的使用是有限度的,让一个接口只服务于一个子模块或业务即可。
[5]迪米特法则(Law of Demeter)
简述
也称最少知道原则(Least Knowledge Principle)。LKP要求一个组件应当和最少的其它组件保持最小程度的耦合,只和直接朋友(直接依赖的组件)通信。
理解
最小程度的耦合
public成员是一定会暴露给外部的成员(当然Java里只要不是private都有不同的暴露范围),之所以要和某组件进行耦合,很多时候就是要去使用它所暴露出来的方法。为了让耦合程度降低,应减少暴露给外部使用的成员,而可以增加使用这些成员的方法,将这个方法暴露给外部。
和最少的组件耦合
如果某组件使用时需要涉及到某些并非它的直接朋友的组件,可以将这样的组件交给其它组件来处理。
使用
为实现LKP,至少应当关注两点:
- 成员的可见范围尽可能地小,如果多个成员方法组成一套业务,可以用一个大的成员方法包起来使用
- 如果一个方法不增加本类的耦合,不产生负面影响,即是只使用自家成员的方法,应当放入本类中
优势
直白地追求低耦合,间接地要求了高内聚。
[6]单一职责原则(Single Responsibility Principle)
简述
应当只有一个原因引起组件的更新,这里组件可以是接口、类、方法。即一个组件只实现一个功能。
理解
SRP的说法非常抽象,”只有一个原因”是指什么样的算一个原因呢?倒不如直接理解成追求高内聚就好了。
使用
在三个层面的组件上都可以追求SRP:
- 接口一定要干净
- 类尽量只承担一个职责,时常思考能否将其分成多个
- 方法亦是能分则分,毕竟可以分成多个private方法,然后由若干对外的方法来调用它们,也追求了LKP
优势
直白地追求高内聚,间接地实现了低耦合。
参考阅读
[1] 六大设计模式–hfreeman2008的专栏中清楚地讲述了设计模式的六大原则,并提供了示例。不过标题起的不太好。”设计原则”和”设计模式本身”不是一样的东西。
[2] 面向对象设计原则–之依赖倒置原则(DIP)中博主用C#写了一个很好的例子阐述DIP。
[3] 设计模式之–依赖倒置原则中博主对DIP的讲解很清楚。
[4] java–依赖、关联、聚合和组合之间区别的理解
[5] 迪米特法则详解–七大面向对象设计原则(6)引入中介类是实现LKP的一种手段,但同时也应当注意到骤升的系统复杂度。
[6] 学习6大设计原则、23种设计模式中每个都简单讲了一下。
补充
在runoob上对六大设计原则的简述中,第六种是合成复用原则,和上篇学的不一样,其意思就是尽量用组合而不是继承。
发布者:admin,转转请注明出处:http://www.yc00.com/news/1692342426a585925.html
评论列表(0条)