0%

OOP七大原则

开闭原则 OCP(Open-Closed Principle)

对扩展开放,对修改关闭

根据开闭原则,在设计一个软件系统模块(类,方法)的时候,应该可以在不修改原有的模块(修改关闭)的基础上,能扩展其功能(扩展开放)。

扩展开放:某模块的功能是可扩展的,则该模块是扩展开放的。软件系统的功能上的可扩展性要求模块是扩展开放的。
修改关闭:某模块被其他模块调用,如果该模块的源代码不允许修改,则该模块修改关闭的。软件系统的功能上的稳定性,持续性要求模块是修改关闭的

开闭原则的实现方法
为了满足开闭原则的对修改关闭原则以及扩展开放原则,应该对软件系统中的不变的部分加以抽象,在面向对象的设计中

  • 可以把这些不变的部分加以抽象成不变的接口,这些不变的接口可以应对未来的扩展;
  • 接口的最小功能设计原则。根据这个原则,原有的接口要么可以应对未来的扩展;不足的部分可以通过定义新的接口来实现;
  • 模块之间的调用通过抽象接口进行,这样即使实现层发生变化,也无需修改调用方的代码。

接口可以被复用,但接口的实现却不一定能被复用。
接口是稳定的,关闭的,但接口的实现是可变的,开放的。
可以通过对接口的不同实现以及类的继承行为等为系统增加新的或改变系统原来的功能,实现软件系统的柔性扩展。

迪米特法则(最少握手) LoD(Law Of Demeter or Principle of Least Knowledge)

一个类对于其他类知道的越少越好,就是说一个对象应当对其他对象有尽可能少的了解,只和朋友通信,不和陌生人说话。

迪米特原则的初衷在于降低类之间的耦合。由于每个类尽量减少对其他类的依赖,因此,很容易使得系统的功能模块功能独立,相互之间不存在(或很少有)依赖关系。

里氏替换原则 LSP(Liskov Substitution Principle)

任何超类出现的地方可以用任一派生类进行替换,实践中可用以校验继承是否有必要以及是否合法

只有满足以下2个条件的OO设计才可被认为是满足了LSP原则:

  • 不应该在代码中出现if/else之类对派生类类型进行判断的条件。
  • 派生类应当可以替换基类并出现在基类能够出现的任何地方,或者说如果我们把代码中使用基类的地方用它的派生类所代替,代码还能正常工作。

同时LSP体现了:

  • 类的继承原则:如果一个派生类的对象可能会在基类出现的地方出现运行错误,则该派生类不应该从该基类继承,或者说,应该重新设计它们之间的关系。
  • 动作正确性保证:从另一个侧面上保证了符合LSP设计原则的类的扩展不会给已有的系统引入新的错误。

重构违反LSP的设计

如果两个具体的类A,B之间的关系违反了LSP 的设计,(假设是从B到A的继承关系),那么根据具体的情况可以在下面的两种重构方案中选择一种:

  • 创建一个新的抽象类C,作为两个具体类的基类,将A,B的共同行为移动到C中来解决问题。
  • 从B到A的继承关系改为关联关系。

单一职责原则  SRP(Single Resposibility Principle)

永远不要让一个类存在多个改变的理由。

要控制类的粒度大小,将对象解耦,提高其内聚性,即一个对象不要承担太多职责,一个方法尽量做好一件事,提高其原子性。

接口隔离原则 ISP(Interface Insolation Principle)

不能强迫用户去依赖那些他们不使用的接口。

  • 按分类设计接口,避免耦合

依赖倒置原则  DIP(Dependency Inversion Principle)

A. 高层模块不应该依赖于低层模块,二者都应该依赖于抽象
B. 抽象不应该依赖于细节,细节应该依赖于抽象
C.针对接口编程,不要针对实现编程

符合DIP原则的操作:

  • 在高层模块与低层模块之间,引入一个抽象接口层
  • 避免依赖传递

组合/聚合复用原则

尽量使用组合/聚合,不要使用类继承

  • 厘清 is-a 与 has-a,区别继承与组合
  • 复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。

在面向对象设计中,有两种基本的办法可以实现复用:第一种是通过组合/聚合,第二种就是通过继承。

什么时候才应该使用继承
只有当以下的条件全部被满足时,才应当使用继承关系:

1)派生类是基类的一个特殊种类,而不是基类的一个角色,也就是区分”Has-A”和”Is-A”。只有”Is-A”关系才符合继承关系,”Has-A”关系应当用聚合来描述。
2)永远不会出现需要将派生类换成另外一个类的派生类的情况。如果不能肯定将来是否会变成另外一个派生类的话,就不要使用继承。
3)派生类具有扩展基类的责任,而不是具有置换掉(override)或注销掉(Nullify)基类的责任。如果一个派生类需要大量的置换掉基类的行为,那么这个类就不应该是这个基类的派生类。
4)只有在分类学角度上有意义时,才可以使用继承。

思考:如何培养code sense

  • 多写多想,从实践中摸索总结
  • 注重代码设计,做好设计再coding
  • 提升bad code识别能力,如重复代码的出现、hard code
  • 基于OOP原则,内化设计模式且与其常用场景结合消化,训练强化的结果

参考自:面向对象设计的七大设计原则详解