【设计模式】——六大原则

六大原则

这里写图片描述

单一职责原则

(Single Responsibility Principle, SRP):就一个类而言,应该仅有一个引起它变化的原因。

【问题】
类T负责两个不同的职责:职责P1,职责P2。当由于职责P1需求发生改变需要修改类T时,有可能会导致原本运行正常的职责P2功能发生故障。
【解决方案】
遵循单一职责原则。分别建立两个类T1、T2,使T1完成职责P1功能,T2完成职责P2功能。这样,当修改T1时,不会使职责P2发生故障的风险。同理,当修改T2时,也不会使职责P1发生故障风险。
【优点】
可以降低类的复杂度,一个类只负责一项职责,其逻辑肯定要比负责多项职责简单的多。
提高类的可读性,提高系统的可维护性。
变更引起的风险降低,变更是必然的,如果单一职责原则遵守的好,当修改一个功能时,可以显著降低对其他功能的影响。


里式替换原则

(Liskov Substitution Principle,LSP):子类型必须能够替换掉它们的父类型。

父类中凡是已经实现好的方法(相对于抽象方法而言),实际上是在设定一系列的规范和契约,虽然它不强制要求所有的子类必须遵从这些契约,但是如果子类对这些非抽象方法任意修改,就会对整个继承体系造成破坏。而里氏替换原则就是表达了这一层含义。
【问题】
有一功能P1,由类A完成,现在需要将功能P1进行扩展,扩展后的功能为P,其中P由原有功能P1与新功能P2组成。新功能P由类A的子类B来完成,则子类B在完成新功能P2的同时,有可能会导致原有功能P1发生故障。
【解决方案】
当使用继承时,遵循里氏替换原则。类B继承类A时,除添加新的方法完成新增功能P2外,尽量不要重写父类A的方法,也尽量不要重载父类A的方法。

里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。


依赖倒转原则

(Dependency Inversion Principle,DIP):高层模块不应该依赖低层模块,两个都应该依赖抽象。抽象不应该依赖细节,细节应该依赖抽象。

【问题】
类A直接依赖类B,假如要将类A改为依赖类C,则必须通过修改类A的代码来达成。这种场景下,类A一般是高层模块,负责复杂的业务逻辑;类B和类C是低层模块,负责基本的原子操作;假如修改类A,会给程序带来不必要的风险。
【解决方案】
将类A修改为依赖接口I,类B和类C各自实现接口I,类A通过接口I间接与类B或者类C发生联系,则会大大降低修改类A的几率。

依赖倒转原则要求我们在程序中要依赖于抽象接口,不要依赖于具体实现。程序代码中传递参数时或在关联关系中,尽量引用层次高的抽象层类,即使用接口和抽象类进行变量类型声明、参数类型声明、方法返回类型声明,以及数据类型的转换等,而不要用具体类来做这些事情。为了确保该原则的应用,一个具体类应当只实现接口或抽象类中声明过的方法,而不要给出多余的方法,否则将无法调用到在子类中增加的新方法。

扫描二维码关注公众号,回复: 2424058 查看本文章

接口隔离原则

(Interface Segregation Principle,ISP):使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口。

【问题】
类A通过接口I依赖类B,类C通过接口I依赖类D,如果接口I对于类A和类B来说不是最小接口,则类B和类D必须去实现他们不需要的方法。
这里写图片描述
这个图的意思是:类A依赖接口I中的方法1、方法2、方法3,类B是对类A依赖的实现。类C依赖接口I中的方法1、方法4、方法5,类D是对类C依赖的实现。对于类B和类D来说,虽然他们都存在着用不到的方法(也就是图中红色字体标记的方法),但由于实现了接口I,所以也必须要实现这些用不到的方法。

这里写图片描述

在使用接口隔离原则时,我们需要注意控制接口的粒度,接口不能太小,如果太小会导致系统中接口泛滥,不利于维护;接口也不能太大,太大的接口将违背接口隔离原则,灵活性较差,使用起来很不方便。一般而言,接口中仅包含为某一类用户定制的方法即可,不应该强迫客户依赖于那些它们不用的方法。


迪米特法则

(Law of Demeter, LoD):如果两个类不必彼此直接通信,那么这两个类就不应当直接的相互作用。如果其中一个类需要调用另一个类的某一个方法的话,可以直接通过第三者转发这个调用。

【问题】
类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。
【解决方案】
尽量降低类与类之间的耦合。

迪米特法则又叫最少知道原则,最早是在1987年由美国Northeastern University的Ian Holland提出。通俗的来讲,就是一个类对自己依赖的类知道的越少越好。也就是说,对于被依赖的类来说,无论逻辑多么复杂,都尽量地的将逻辑封装在类的内部,对外除了提供的public方法,不对外泄漏任何信息。迪米特法则还有一个更简单的定义:只与直接的朋友通信。首先来解释一下什么是直接的朋友:每个对象都会与其他对象有耦合关系,只要两个对象之间有耦合关系,我们就说这两个对象之间是朋友关系。耦合的方式很多,依赖、关联、组合、聚合等。其中,我们称出现成员变量、方法参数、方法返回值中的类为直接的朋友,而出现在局部变量中的类则不是直接的朋友。也就是说,陌生的类最好不要作为局部变量的形式出现在类的内部。因此,应用迪米特法则有可能造成的一个后果就是:系统中存在大量的中介类,这些类之所以存在完全是为了传递类之间的相互调用关系——这在一定程度上增加了系统的复杂度。


开闭原则

(Open-Closed Principle,OCP):是说软件实体(类、模块、函数等等)应该卡伊扩展,但是不可修改。

【问题】
在软件的生命周期内,因为变化、升级和维护等原因需要对软件原有代码进行修改时,可能会给旧代码中引入错误,也可能会使我们不得不对整个功能进行重构,并且需要原有代码经过重新测试。
【解决方案】
当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。

为了满足开闭原则,需要对系统进行抽象化设计,抽象化是开闭原则的关键。无须对抽象层进行任何改动,只需要增加新的具体类来实现新的业务功能即可,实现在不修改已有代码的基础上扩展系统的功能,达到开闭原则的要求。使得软件系统在拥有适应性和灵活性的同时具备较好的稳定性和延续性。


合成聚合复用原则

(Composite Reuse Principle, CARP):尽量使用合成/聚合,尽量不要使用类继承。

【问题】
软件公司开发人员在初期的系统设计中,考虑到客户数量不多,系统采用MySQL作为数据库,与数据库操作有关的A类等都需要连接数据库,连接数据库的方法封装在B类中,由于需要重用B类的方法,设计人员将A作为B类的子类,随着客户数量的增加,系统决定升级为Oracle数据库,因此需要增加一个新的C类来连接Oracle数据库,由于在初始设计方案中A和B之间是继承关系,因此在更换数据库连接方式时需要修改A类的源代码,将A作为C的子类,这将违反开闭原则。
【解决方案】
A和B之间的关系由继承关系变为关联关系,采用依赖注入的方式将B对象注入到A中,可以使用构造注入,也可以使用Setter注入。如果需要对B的功能进行扩展,可以通过其子类来实现,如通过子类C来连接Oracle数据库。由于A针对B编程,根据里氏代换原则,B子类的对象可以覆盖B对象,只需在A中注入子类对象即可使用子类所扩展的方法。例如在A中注入C对象,即可实现Oracle数据库连接,原有代码无须进行修改,而且还可以很灵活地增加新的数据库连接方式。

合成/聚合复用原则就是在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分;新的对象通过向这些对象的委派达到复用已有功能的目的。


说了这么多,我来举个栗子

一个非常生动的栗子
这里写图片描述

猜你喜欢

转载自blog.csdn.net/mirabellezwh/article/details/80813174
今日推荐