七大设计模式原则

1. 单一原则(SRP)

单一职责原则 : Single Responsibility principle

它要求一个类或者模块应该只负责一个特定的功能, 这有助于降低类之间的耦合度, 提高代码的可读性和了维护性

单一职责原则反例 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class User {
private String name;

// Getter 和 Setter 方法
public String sayHi() {
// 计算员工工资的逻辑
return "hi" + this.name;
}

public void getUserById() {
// 根基 id 获得用户
}
}

一个用户的实体应该围绕着他的本身进行实现方法, 在这个类中, 这里包括了和这个类无关的逻辑

类的职责是确定的吗 ?

有的类的单一职责是有时候是很难确定的, 需要根据业务视情况而定

类的职责是否越单一越好 ?

不是

1
2
3
4
5
6
7
8
9
public class Serialization  {
private String type;
public String serialize(Object obj) {
// 使用type 方式序列化 obj
}
public Object deserialize(String text) {
// 使用type类型反序列化 text
}
}

我们可以将这个类拆分为两个独立的部分 : 1. 序列化 2. 反序列化

但是当我们拆分之后, 则两个职责就失去了一个沟通的桥梁, 拆分之后还

2. 开闭原则

开闭原则: Open Closed Principle

开闭原则简单来说就是: 对扩展开放, 对修改关闭

当我们要新增加一个功能时, 不需要去修改原有的代码

比如 当我们需要替换 SpringBoot 的默认日志实现的时候, 我们不需要去更改代码, 我们只需要增加一个pom 依赖就可以

开闭原则是一点代码都不去修改吗 ?

开闭原则是要求我们尽量去减少对原有代码的修改

我们无法做到真的不去修改代码

当我们对当当前的功能上进行适当的改进的时候, 也可以说是遵循了开闭原则

如何践行开闭原则 ?

为了更好的去实现开闭原则, 我们需要将可变的部分封装为一个独立的模块, 然后找寻这些模块共同的部分, 抽象出一个公共的部分,. 尽量屏蔽上层对下层的强依赖

有以下几种实现方式 :

  • 抽象和封装
  • 依赖注入
  • 使用设计模式
  • 使用事件机制
  • 使用插件
  • 使用消息

3. 里氏替换原则

里式替换原则: Liskov Substitution Principle

通俗来说 : 子类对象能够替换程序中父类对象出现的任何地方,并且保证原来程序的逻辑行为不变及正确性不被破坏。

实例 : 对于接口的实现 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 飞行行为接口
public interface Flyable {
void fly();
}

// 基类:鸟类
public class Bird {
}

// 子类:能飞的鸟类
public class FlyingBird extends Bird implements Flyable {
@Override
public void fly() {
System.out.println("I can fly");
}
}

// 子类:企鹅类,不实现Flyable接口
public class Penguin extends Bird {
}

反例的特征

  1. 覆盖了父类的方法

  2. 对父类添加了新的约束 比如 单调栈对于栈来说就增加了一个约束

  3. 子类与基类之间缺乏 “ is-a”关系( 子类是一个基类)

4. 接口隔离原则

接口隔离原则: Interface Segregation Principle

接口隔离原则: 客户端不应该强迫依赖它不需要的接口。

此外它还有一个定义 : 一个类对另一个类的依赖应该建立在最小的接口上

这要求程序员尽量将臃肿庞大的接口拆分成更小的和更具体的接口

接口隔离原则的优点 ?

  1. 将臃肿庞大的接口分解为多个粒度小的接口,可以预防外来变更的扩散,提高系统的灵活性和可维护性。
  2. 接口隔离提高了系统的内聚性,减少了对外交互,降低了系统的耦合性。
  3. 如果接口的粒度大小定义合理,能够保证系统的稳定性;但是,如果定义过小,则会造成接口数量过多,使设计复杂化;如果定义太大,灵活性降低,无法提供定制服务,给整体项目带来无法预料的风险。
  4. 使用多个专门的接口还能够体现对象的层次,因为可以通过接口的继承,实现对总接口的定义。
  5. 能减少项目工程中的代码冗余。过大的大接口里面通常放置许多不用的方法,当实现这个接口的时候,被迫设计冗余的代码。

反例 :

1
2
3
4
5
6
public interface action {
void fly();
void walk();
void say();
void look();
}

当一个动物如果想要行动, 就需要实现里面的全部接口, 有的是完全没有必要的, 比如狗的没有fly 的动作的

所以这个action 的粒度就偏大, 我们应该进行拆分

5. 依赖倒置原则

依赖倒置原则 : Dependency Inversion Principle

依赖倒置原则 : 要依赖于抽象而不是具体实现

依赖倒置原则有两个关键点:

  • 高层模块不应该依赖于低层模块,它们都应该依赖于抽象。
  • 抽象不应该依赖于具体实现,具体实现应该依赖于抽象。

实例 : Ioc 容器 MVC 中的 service intface mapper intface

6. 合成/聚合复用原则

合成/聚合复用原则 Composite/Aggregate Reuse Principle

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

它的设计原则是:要尽量使用合成/聚合,尽量不要使用继承。

聚合概念:

聚合用来表示“拥有”关系或者整体与部分的关系。代表部分的对象有可能会被多个代表整体的对象所共享,而且不一定会随着某个代表整体的对象被销毁或破坏而被销毁或破坏,部分的生命周期可以超越整体。

例如,Iphone5和IOS,当Iphone5删除后,IOS还能存在,IOS可以被Iphone6引用。

合成概念:

合成用来表示一种强得多的“拥有”关系。在一个合成关系里,部分和整体的生命周期是一样的。一个合成的新对象完全拥有对其组成部分的支配权,包括它们的创建和湮灭等。

组合与继承的区别 ?

在面向对象编程中,复用已有模块功能的方法有:组合与继承。

继承复用的特点:实现简单、易于扩展,破坏系统的封装性。从父类继承过来的实现是静态的,不可能通过运行时发生改变,没有足够的灵活性。

组合聚合复用:耦合度相对较低,选择性地调用成员对象操作,可以在运行时动态进行。

7 . 迪米特法则 (最少知识原则)

迪米特法则 Law of Demeter

核心思想是:一个对象应该尽量少地了解其他对象,降低对象之间的耦合度,从而提高代码的可维护性和可扩展性。

简单来说就是 : 两个类之间尽量不要直接依赖,如果必须依赖,最好只依赖必要的接口。

因此,应用迪米特法则有可能造成的一个后果就是:系统中存在大量的中介类,这些类之所以存在完全是为了传递类之间的相互调用关系——这在一定程度上增加了系统的复杂度。

迪米特原则主要强调只和朋友交流,不和陌生人说话。

那么,如何界定朋友?如何界定陌生人呢 ?

迪米特法则指出,做为“朋友”的条件为:

  • 当前对象本身(this);
  • 被当做当前对象的方法的参数传入进来的对象;
  • 当前对象的方法所创建或者实例化的任何对象;
  • 当前对象的任何组件:被当前对象的实例变量引用的任何对象;
  • 任何一个对象,如果满足上面的条件之一,就是当前对象的“朋友”,否则就是“陌生人”。

假如你不认识你朋友 b 的朋友 c, 那你不要和 c 进行直接的沟通, 要经过 b 进行转达

总结 :

单一职责原则便是高内聚的基本思想;组合聚合原则来降低使用继承的强耦合性;迪米特法则 强调高内聚, 低耦合;接口隔离原则仍是体现高内聚思想;里氏替换原则依赖倒置原则则助力实现高内聚弱耦合效果;开闭原则则是高内聚弱耦合的应用。


七大设计模式原则
http://shadowzlh.top/2023/08/30/七大设计模式原则/
作者
shadowzlh
发布于
2023年8月30日
许可协议