本文摘自书籍《设计模式》
此系列文章GitHub地址
面向对象设计原则
面向对象设计原则是设计模式的基础,每一种设计模式都符合一种或多种原则。
单一职责原则(SRP)
一个对象应该只包含单一的职责,并且该职责被完整的封装在一个类中。
类的职责
- 数据职责 — 通过属性来体现。
- 行为职责 — 通过方法来体现。
分析
一个类承担的职责越多,它被复用的可能性越小,这些职责耦合在一起,当其中一个职责变化时,可能会影响其他职责的运作。因此,将职责分离,不同的职责封装在不同的类中,即单一职责原则。
作用
单一职责原则是实现高内聚、低耦合的知道方针,在很对重构手法中都能找到它的身影,它是最简单但最难运用得原则。
开闭原则(OCP)
一个软件实体应该对扩展开放,对修改关闭。即不修改源码的情况下可以对模块进行扩展。
软件实体
指一个软件模块、一个由多个类组成的局部结构或一个独立的类。
分析
任何软件都需要面临一个重要的问题,那就是需求的变化,如果软件设计符合开闭原则,那就可以非常方便的对系统进行扩展,而且在扩展时无需修改现有代码,使得软件系统在拥有适应性和灵活性的同时具备较好的稳定性和延续性。
为了满足开闭原则,需要对系统进行抽象化设计,抽象化是开闭原则的关键。在 Java 中,可以为系统定义一个相对稳定的抽象层,而将不同的实现行为在具体实现层中完成。
百分百的开闭原则很难达到,但是要尽可能是系统设计符合开闭原则。后面引入的里氏代换原则、依赖倒转原则等都是开闭原则的实现方法。
里氏代换原则(LSP)
所有引用基类(父类)的地方必须能透明的使用其子类的对象。意思是由于使用基类的地方都可以使用其子类对象,因此在程序中尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象替换父类对象。
Father obj;
obj = new Children();
分析
- 子类所有方法必须在父类中声明,或子类必须实现父类中声明的所有方法。根据里氏代换原则,为了保证系统的扩展性,在程序中通常使用父类来进行定义,如果一个方法只存在于子类中,父类中不提供相应的声明,则无法在父类对象中直接使用此方法。
- 尽量把父类设计为抽象类或接口,让子类继承父类或事项父接口。在进行扩展时,我们甚至可以不修改子类的实现,通过新增一个子类来新增功能。
依赖倒转原则(DIP)
高层模块不应该依赖低层模块,它们都应该依赖抽象。抽象不应该依赖细节,细节应该依赖于抽象。换句话说,针对接口编程,不要针对实现编程。
分析
在程序代码中传递参数时或组合聚合关系中,尽量引用层次高的抽象层类,即使用接口和抽象类进行变量类型声明、参数类型声明、方法返回类型声明以及数据类型转换等。
接口隔离原则(ISP)
一个接口太大,则需要将它分割成一些更细小的接口,使用该接口的客户端仅需知道与之相关的方法即可。实质上,接口隔离原则是指使用多个专门的接口,而不使用单一的总接口。每一个接口应该承担一种相对独立的角色,干该干的事,不干不该干的事。
分析
使用接口隔离原则拆分接口时,首先必须满足单一职责原则,将一组相关的操作定义在一个接口中,且在满足高内聚的前提下,接口中的方法越少越好。
合成复用原则(CRP)
又称为组合/聚合复用原则:尽量使用对象组合,而不是继承来达到复用的目的。
分析
将一个类的对象作为另一个类的对象的一部分,或者说一个对象是由另一个或几个对象组合而成。由于组合或聚合关系可以将已有的对象纳入到新对象中,使之成为新对象的一部分,因此新对象可以调用已有对象的功能,这样做可以是的成员对象的内部细节对新对象是不可见的,所以这种复用也称为“黑箱”复用,相对继承复用而言,其耦合度相对较低,成员对象的变化对新对象的影响不大,可以在新对象中根据实际需要选择性的调用成员对象的操作。
迪米特法则(LoD)
一个软件实体应该尽可能的与其它实体发生相互作用。另一种说法:至于你的直接朋友通信。对于一个对象,它的朋友包括:
- 当前对象本身
- 以参数形式传入到当前对象的方法中的对象
- 当前对象的成员对象
- 如果成员对象为集合,那么集合中的元素都是朋友
- 当前对象所创建的对象
分析
在迪米特法则运用中,应该注意以下几点:
- 在类的划分上,应当尽量创建松耦合的类,类之间的耦合越低,就越有利于复用。
- 在类的结构设计上,每一个类都应该尽量降低成员变量和成员函数的访问权限。
- 在类的设计上,只要有可能,一个类型应当设计成不变类。
- 在对其他类的引用上,一个对象对其他对象的引用应当降低到最低。