设计模式基础

设计模式

什么是设计模式?

设计模式 Design Pattern 是⼀套被反复使⽤、多数⼈知晓的、经过分类编⽬的、代码设计经验的总结,使⽤设计模式是为了可重⽤代码、让代码更容易被他⼈理解、保证代码可靠性。

设计模式的4个元素

pattern name模式名称,一个助记名,它用两个词来描述模式的问题、解决方案和效果。

problem问题,描述了应该在何时使用模式。

solution解决方案,描述了设计的组成成分,它们之间的相互关系及各自的职责和协作方式。

consequence效果,描述了模式应用的效果及使用模式应权衡的问题。

image-20240103132730242

设计模式原则

类之间的关系

image-20220829213717053

(1)单一职责原则(Single Responsibility Principle)

定义:一个类应该只包含单一职责,既是信息内聚又是功能内聚,信息与行为除了要集中外,还要联合起来表达一个内聚的概念。

解释:简单的讲,就是一个类或接口只负责一个”职责”。如果一个类有一个以上的职责,这些职责就耦合在一起了。

优点:

1.可以降低类的复杂度

2.提高类的可读性,系统的可维护性

3.降低变更的时候对其他功能的影响(变更不可避免)

缺点:如果把职责划分过细反而会造成接口和实现类的数量剧增,反而不利于维护和阅读。

(2)里氏替换原则(Listkov Substitution Principle)

定义:子类型能够替换基类型并起同样的作用。(最典型的反例:正方形和长方形)

为了满足LSP:

  1. 子类的前置方法条件(形参)必须与超类方法的前置条件相同或者要求更少。

  2. 子类的后置条件(返回值)必须与超类的后置条件相同或者要求更多。

解释:父类出现的地方,都可以用子类替换,程序不会出错,且程序的行为与替换之前一样。

优点:

  1. 里氏替换原则增强了程序的健壮性,使程序传递不同的子类完成不同的逻辑而不产生错误.
  2. 里氏替换原则其实也就是对继承的一种规范,实现抽象化的一种规范。

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

定义:高层模块不应该依赖于低层模块,二者都应该依赖于抽象;抽象不应该依赖于具体的细节;细节应该依赖于抽象。

解释:针对接口编程

优点:

传统过程化程序设计所创建的依赖关系,策略依赖于细节,这是糟糕的,因为策略受到细节改变的影响。依赖倒置原则使细节和策略都依赖于抽象,抽象的稳定性决定了系统的稳定性。

编程注意:

低层模块尽量都要有抽象类或接口,或者两者都有。

变量的声明类型尽量是抽象类或接口。

使用继承时遵循里氏替换原则。

三中实现方式:

1.通过构造函数传递依赖对象;

2.通过setter方法传递依赖对象;

3.接口声明实现依赖对象,也叫接口注入

(4)接口隔离原则(Interface Segregation Principle)

定义:客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。

编程注意:

  • 接口尽量小,但是也有一个限度。(接口太小造成接口数量过多)

  • 设计接口的时候,只为依赖于他的类提供其需要的方法。

  • 提高内聚,减少对外的交互

(5)迪米特法则(Law of Demeter)/最少知识原则(Least Knowledge Principle)

定义:一个对象应该对其他对象保持最少的了解。

解释:只和直接朋友交流。出现在成员变量、方法参数、方法返回值中的类为直接的朋友。

优点:降低了类之间的耦合。

缺点:过分的使用迪米特原则,会产生大量这样的中介和传递类,导致系统复杂度变大。

(6)开闭原则(Open-Closed Principle)

定义:一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。

解释:当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。

关系

OCP开闭原则是面向对象设计的目标,DIP依赖倒置原则是主要的实现手段,LSP里氏替换原则是DIP依赖倒置原则的基础

LSP(保证)->DIP(实现机制)->OCP(目标)

多态也是OCP的实现机制之一

(7)好莱坞原则(The Hollywood Principle)

定义: ⼦类不显式调⽤⽗类的⽅法,⽽是通过覆盖⽗类的⽅法来实现某些具体业务逻辑,通过⽗类来调⽤⼦类

(8)组合复用原则(Composite Reuse Principle)

尽量使用合成/聚合的方式,而不是使用继承。

在不满足LSP时,用组合来代替继承实现代码的复用。

子类应该能替换基类而起相同的作用,只为了复用代码而不为了组织类型差异的继承用法往往是不符合LSP原则,需要使用组合代替继承(既能复用代码又能保持接口灵活性)

继承与组合对比

继承:

优点:能很好的完成接口与实现的分离,子类不但继承了父类的接口还继承了实现,可以更好地进行代码重用。

缺点:

1.耦合度高,继承时所有子类和父类都存在公共接口的耦合,当父类接口发生变化时,子类接口也要改动,会影响到Client代码,不利于类的拓展与维护。

2.限制了复用的灵活性,子类创建对象时(编译时)就决定了实现的选择,无法动态修改。

组合:

1.利用组合既能复用代码又能保持接口的灵活性。

2.组合中不存在接口耦合,当组合类接口发生变化时不会影响Client代码。

3.组合类能实现动态创建,动态配置,动态销毁。


设计模式基础
http://example.com/2023/10/15/计算机基础/设计模式/01-设计模式基础/
作者
PALE13
发布于
2023年10月15日
许可协议