数了数图,有25种,不对劲,带ok的那两个模式可以去掉。
SOLID
简而言之就是对扩展开放,对修改关闭。比如要增加新的功能,通过增加一个实现类来实现,而不是修改原有的代码来实现。比如Android app中将图片获取功能抽象出一个接口,接口中实现一个获取图片方法,增加一个类实现接口,内部使用Universal Image Loader库进行图片获取;后续比如Glide库更好,这时候增加一个新的类实现这个接口,内部图片获取方式改为Glide即可完成图片获取方式 的替换,而避免了原有代码大量的修改。会更加的灵活。
定义:一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。模块应尽量在不修改原(是"原",指原来的代码)代码的情况下进行扩展。
比如Activity或Fragment中一般用来显示和交互,如果里面有网络请求的逻辑,文件读写的逻辑,就违反了SRP,引起Activity变化的原因就会增加,从而造成Acttivity代码臃肿,难以维护,混乱和复杂。
定义:指一个类或者模块应该有且只有一个改变的原因。如果一个类承担的职责过多,就等于把这些职责耦合在一起了。一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力。这种耦合会导致脆弱的设计,当发生变化时,设计会遭受到意想不到的破坏。而如果想要避免这种现象的发生,就要尽可能的遵守单一职责原则。此原则的核心就是解耦和增强内聚性。
定义:所有引用基类的地方必须能透明地使用其子类的对象,也可以简单理解为任何基类可以出现的地方,子类一定可以出现。
只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对"开-闭"原则的补充。实现"开-闭"原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。当然,如果反过来,软件单位使用的是一个子类对象的话,那么它不一定能够使用基类对象。举个很简单的例子说明这个问题:如果一个方法接收Map类型参数,那么它一定可以接收Map
的子类参数例如HashMap
、LinkedHashMap
、ConcurrentHashMap
类型的参数;但是反过来,如果另一个方法只接收HashMap
类型的参数,那么它一定不能接收所有Map
类型的参数,否则它可以接收LinkedHashMap
、ConcurrentHashMap
类型的参数。
定义:程序要依赖于抽象接口,不要依赖于具体实现。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。
意义:依赖倒转原则要求我们在程序代码中传递参数时或在关联关系中,尽量引用层次高的抽象层类,即使用接口和抽象类进行变量类型声明、参数类型声明、方法返回类型声明,以及数据类型的转换等,而不要用具体类来做这些事情。为了确保该原则的应用,一个具体类应当只实现接口或抽象类中声明过的方法,而不要给出多余的方法,否则将无法调用到在子类中增加的新方法。在引入抽象层后,系统将具有很好的灵活性,在程序中尽量使用抽象层进行编程,而将具体类写在配置文件中,这样一来,如果系统行为发生变化,只需要对抽象层进行扩展,并修改配置文件,而无须修改原有系统的源代码,在不修改的情况下来扩展系统的功能,满足开闭原则的要求。
定义:客户端不应该依赖它不需要的接口,类间的依赖关系应该建立在最小的接口上。简单来说就是建立单一的接口,不要建立臃肿庞大的接口。也就是接口尽量细化,同时接口中的方法尽量少。
如何看待接口隔离原则和单一职责原则
单一职责原则注重的是类和接口的职责单一,这里职责是从业务逻辑上划分的,但是在接口隔离原则要求当一个接口太大时,我们需要将它分割成一些更细小的接口,使用该接口的客户端仅需知道与之相关的方法即可。也就是说,我们在设计接口的时候有可能满足单一职责原则但是不满足接口隔离原则。
接口隔离原则的规范
定义:尽量使用合成/聚合,而不是通过继承达到复用的目的。
合成/聚合复用原则就是在一个新的对象里面使用一些已有的对象,使之成为新对象的一部分;新的对象通过向内部持有的这些对象的委派达到复用已有功能的目的,而不是通过继承来获得已有的功能。
聚合(Aggregate)
聚合表示一种弱的"拥有"关系,一般表现为松散的整体和部分的关系,其实,所谓整体和部分也可以是完全不相关的。例如A对象持有B对象,B对象并不是A对象的一部分,也就是B对象的生命周期是B对象自身管理,和A对象不相关。比如一群大雁和一只大雁,群体和个体的弱拥有关系。
合成/组合(Composite)
合成表示一种强的"拥有"关系,一般表现为严格的整体和部分的关系,部分和整体的生命周期是一样的。比如一头牛和牛头则是一种整体和部分的强拥有关系。
为什么要用合成/聚合来替代继承达到复用的目的
继承复用破坏封装特性,因为继承将基类的实现细节暴露给派生类,基类的内部细节通常对子类来说是可见的,这种复用也称为"白箱复用"。这里有一个明显的问题是:派生类继承自基类,如果基类的实现发生改变,将会影响到所有派生类的实现;如果从基类继承而来的实现是静态的,不可能在运行时发生改变,不够灵活。
由于合成或聚合关系可以将已有的对象,一般叫成员对象,纳入到新对象中,使之成为新对象的一部分,因此新对象可以调用已有对象的功能,这样做可以使得成员对象的内部实现细节对于新对象不可见,所以这种复用又称为"黑箱"复用,相对继承关系而言,其耦合度相对较低,成员对象的变化对新对象的影响不大,可以在新对象中根据实际需要有选择性地调用成员对象的操作;合成/聚合复用可以在运行时动态进行,新对象可以动态地引用与成员对象类型相同的其他对象。
定义:一个软件实体应当尽可能少地与其他实体发生相互作用。每一个软件单位对其他的单位都只有最少的了解,而且局限于那些与本单位密切相关的软件单位。迪米特法则的初衷在于降低类之间的耦合。由于每个类尽量减少对其他类的依赖,因此,很容易使得系统的功能模块功能独立,相互之间不存在(或很少有)依赖关系。迪米特法则不希望类之间建立直接的联系。如果真的有需要建立联系,也希望能通过它的友元类(中间类或者跳转类)来转达。
规则
意义
迪米特法则的核心观念就是类间解耦,也就降低类之间的耦合,只有类处于弱耦合状态,类的复用率才会提高。所谓降低类间耦合,实际上就是尽量减少对象之间的交互,如果两个对象之间不必彼此直接通信,那么这两个对象就不应当发生任何直接的相互作用,如果其中的一个对象需要调用另一个对象的某一个方法的话,可以通过第三者转发这个调用。简言之,就是通过引入一个合理的第三者来降低现有对象之间的耦合度。但是这样会引发一个问题,有可能产生大量的中间类或者跳转类,导致系统的复杂性提高,可维护性降低。如果一味追求极度解耦,那么最终有可能变成面向字节码编程甚至是面向二进制的0和1编程。
说实话,设计模式的七大原则理解是比较困难的,我们在设计模式的学习和应用中经常会听到或者看到"XXX模式符合XXX原则"、"YYY模式不符合YYY原则"这样的语句。因此,为了分析设计模式的合理性和完善我们日常的编码,掌握和理解这七大原则是十分必要的。
本文对其他博客内容复制粘贴严重,感谢大佬,向原作者致敬,引用链接记在下面。在开发中设计模式原则方面目前也太偏重于理论,在实践中有些原则的体会是没有的或是非常浅显的。
参考:
https://cloud.tencent.com/developer/article/1665573
https://blog.csdn.net/SEU_Calvin/article/details/66994321
xmind画图
https://blog.csdn.net/skytruine/article/details/105828748
类图
https://blog.csdn.net/horsee/article/details/113883818
设计模式的原则
https://cloud.tencent.com/developer/article/1650116