改变编码游戏规则:揭秘23种设计模式的魅力和实用性

本文最后更新于:1 年前

勇气不是不感到害怕,而是即便颤抖也选择站出来。

破冰

  • 🥇 推荐阅读:(2023/10/20 午)
🍖 两万字盘点被玩烂了的 9 种设计模式 - 掘金 (juejin.cn)
👏 23 种设计模式详解 - 掘金 (juejin.cn)
🍜 「聊设计模式」之抽象工厂模式(Abstract Factory) - 掘金 (juejin.cn)

什么是设计模式

  • 设计模式是软件设计中常见的问题解决方案的归纳总结,是在特定情境下的经验性的解决方案。
  • 它们被广泛接受和应用于软件开发中,旨在提高代码的可重用性、可维护性和灵活性。

七大原则

  • 设计模式的七大原则是作为设计模式的基础准则,可以用来指导设计模式的选择和应用。这些原则是:
    1. 单一职责原则(SRP):一个类应该只有一个引起它变化的原因。
    2. 开放-封闭原则(OCP):软件实体(类、模块、函数等)应该对扩展是开放的,对修改是封闭的。
    3. 里氏替换原则(LSP):子类型必须能够替换其基类型,而不会影响程序的正确性。
    4. 接口隔离原则(ISP):建立最小的依赖,不要依赖不需要的接口。
    5. 依赖倒置原则(DIP):依赖于抽象,而不是具体实现。
    6. 迪米特法则(LoD):一个对象应该对其他对象保持最少的了解。
    7. 组合/聚合复用原则(CARP):优先使用组合和聚合,而不是继承。

分类

  • 设计模式根据功能和用途可以分为三大分类:

    1. 创建型设计模式:这些模式关注对象的创建机制,主要包括工厂模式抽象工厂模式单例模式原型模式建造者模式等。
    2. 结构型设计模式:这些模式关注如何组合和使用对象,主要包括适配器模式装饰器模式代理模式组合模式享元模式桥接模式等。
    3. 行为型设计模式:这些模式关注对象之间的通信和协作方式,主要包括策略模式模板方法模式观察者模式迭代器模式访问者模式命令模式备忘录模式状态模式解释器模式等。
  • 这些分类和原则将设计模式划分到更具体的范畴,并提供了指导设计和实施设计模式的准则。理解和应用这些原则和分类有助于开发人员更好地使用设计模式来解决问题。

正文

单例模式

懒汉式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Singleton {
// 1.静态变量
private static Singleton instance;

// 2.构造器私有化
private Singleton() {
}

// 3.对外暴露接口,实例化对象
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
  • 以上是标准的懒汉式实现单例模式,主要有三个步骤:(2023/09/19 午)
    • 静态变量 private static Singleton instance;
    1
    2
    3
    - 在类的静态区域中创建一个私有的静态变量 `instance`,用于存储单例对象的实例。
    - 静态变量保证了在类的所有实例中只存在一个副本。
    - 这里将该静态变量设置为私有是为了确保无法通过类的外部直接访问和修改该变量。
    • 构造器私有化 private Singleton() {}
    1
    2
    - 将类的构造器私有化,防止通过 `new Singleton()` 在类的外部创建实例。
    - 这样的设计强制要求只能通过类的内部调用来获取类的实例。
    • 对外暴露接口 public static Singleton getInstance()
    1
    2
    3
    4
    5
    - 提供一个公共的静态方法 `getInstance()`,用于获取单例对象的实例。
    - 在方法中,首先判断 `instance` 是否为空。
    - 如果为空,即还未实例化对象,则在内部实例化一个对象,并将其赋值给 `instance`
    - 最后返回 `instance`,无论是首次调用还是后续调用,都返回同一个实例。
    - 这样的设计实现了懒汉式的单例模式,只有在实际需要的时候才会创建对象,节省了资源。但是在多线程环境下,可能会存在线程安全问题,需要进行额外的处理来保证线程安全,如使用加锁机制、双重检查等。

饿汉式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Singleton2 {
// 1.静态变量 创建一个实例对象
private static Singleton2 instance = new Singleton2();
;

// 2.构造器私有化
private Singleton2() {
}

// 3.对外暴露接口,实例化对象
public static Singleton2 getInstance() {
return instance;
}
}
  • 这跟懒汉式唯一的区别就是:他提前把对象 new 出来,这样别人哪怕是第一次获取这个类对象的时候直接就存在这个类了,省去了创建类这一步的开销。

加锁优化(空间换时间)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Singleton {
// 1.静态变量
private static Singleton instance;

// 2.构造器私有化
private Singleton() {
}

// 3.对外暴露接口,实例化对象
public synchronized static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
  • 这是一种典型的时间换空间的写法,不管三七二十一,每次创建实例时先锁起来,再进行判断,严重降低了系统的处理速度

双检锁优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Singleton {
// 1.静态变量
private static Singleton instance;

// 2.构造器私有化
private Singleton() {
}

// 3.对外暴露接口,实例化对象
public static Singleton getInstance() {
// 检查实例是否存在 不存在即进入同步块
if (instance == null) {
synchronized (Singleton.class) {
// 再次检查实例是否存在 不存在即创建实例
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
  • 将 synchronized 关键字加在了内部,也就是说当调用的时候是不需要加锁的,只有在 instance 为 null,并创建对象的时候才需要加锁,性能有一定的提升

  • 但是这样仍然会存在问题,创建一个实例分为三步骤:

    • 分配内存空间、初始化对象、实例指向该内存空间
    • 这样才算创建了一个实例
  • 情景:

    • 实例为空,线程 A 创建实例

    • 由于 JVM 内部的优化机制,JVM 先画出了一些分配给 Singleton 实例的空白内存,并赋值给 instance 成员(注意此时 JVM 没有开始初始化这个实例)

    • 这个时候线程 A 释放锁,线程 A 退出

    • 线程 B 拿到锁,发现已存在实例,直接返回该实例,线程 B 退出

    • 而这个实例还没有初始化完成,错误发生(2023/09/19 午)

Volatile 修饰

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Singleton {
// 1.静态变量
private volatile static Singleton instance;

// 2.构造器私有化
private Singleton() {
}

// 3.对外暴露接口,实例化对象
public static Singleton getInstance() {
// 检查实例是否存在 不存在即进入同步块
if (instance == null) {
synchronized (Singleton.class) {
// 再次检查实例是否存在 不存在即创建实例
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
  • 通过 volatile 修饰的变量,不会被线程本地缓存,所有线程对该对象的读写都会第一时间同步到主内存,从而保证多个线程间该对象的准确性

volatile 的作用

  • 防止指令重排序,因为 instance = new Singleton()不是原子操作

  • 保证内存可见

  • 但是由于 volatile 关键字可能会屏蔽掉虚拟机中一些必要的代码优化,所以运行效率并不是很高,还有更优的写法吗?

静态内部类实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Singleton3 {
// 构造方法私有化
private Singleton3() {
}

// 此处使用一个内部类来维护单例
private static class SingletonFactory {
private static Singleton3 instance = new Singleton3();
}

// 获取实例
public static Singleton3 getInstance() {
return SingletonFactory.instance;
}
}
  • 使用内部类来维护单例的实现,JVM 内部的机制能够保证当一个类被加载的时候,这个类的加载过程是线程互斥的。

  • 这样当我们第一次调用 getInstance 的时候,JVM 能够帮我们保证 instance 只被创建一次,并且会保证把赋值给 instance 的内存初始化完毕, 这样我们就不用担心上面的问题

  • 同时该方法也只会在第一次调用的时候使用互斥机制,这样就解决了低性能问题

枚举

1
2
3
public enum Singleton4 {
INSTANCE;
}
  • 使用枚举来实现单实例控制会更加简洁,而且 JVM 从根本上提供保障,绝对防止多次实例化,是更简洁、高效、安全的实现单例的方式

  • 枚举就是天生用来实现单例模式的(2023/09/19 午)

适配器模式

  • 我们有两种实现方式:
  • 类适配器模式(Class Adapter Pattern):

    • 在类适配器模式中,适配器类同时继承目标类和适配者类。通过继承适配者类,适配器类可以调用适配者类的方法,并通过实现目标接口,将适配者类的方法适配成目标接口的方法
  • 对象适配器模式(Object Adapter Pattern):

    • 在对象适配器模式中,适配器类持有一个适配者类的实例,并实现目标接口。通过调用适配者类的方法来实现目标接口的方法
  • 以下是代码实现:

对象适配器模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 目标接口(现有的接口)
interface Target {
void request();
}

// 适配者(用户提供的接口)
class Adaptee {
public void specificRequest() {
System.out.println("调用了适配者的specificRequest方法");
System.out.println();
}
}

// 适配器(转接器)
class Adapter implements Target {
private final Adaptee adaptee;

public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}

@Override
public void request() {
adaptee.specificRequest();
}
}

类适配器模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 目标接口(现有的接口)
interface Target {
void request();
}

// 适配者(用户提供的接口)
class Adaptee {
public void specificRequest() {
System.out.println("调用了适配者的specificRequest方法");
System.out.println();
}
}

// 适配器(转接器)
class Adapter2 extends Adaptee implements Target {
public Adapter2() {}

@Override
public void request() {
this.specificRequest();
}
}
  • 实现起来,这两种方法没有多大区别
    • 类适配器模式:通过继承,直接调用适配者的方法
    • 对象适配器模式:通过创建一个适配者实例对象,使用这个对象调用适配者的方法
  • MemorySearch 聚合搜索平台 项目中,我就使用到了适配器模式,用来统一处理前端请求 (2023/09/19 午)

简单工厂模式

  • 简单工厂的核心思想是:将对象的创建过程封装在一个工厂中,只需要根据用户传递的参数来选择实例化/创建哪一个对象

  • 定义一个抽象产品类:(2023/09/22 午)
1
2
3
4
5
6
7
8
9
10
11
12
package com.example.javaDesignPattern.simpleFactory;

/**
* 抽象产品类
*
* @author 邓哈哈
* @version 1.0
* @date 2023/9/18 15:36
*/
public interface Product {
void execute();
}
  • 分别定义两个具体的产品类 ConcreteProductA、ConcreteProductB:
1
2
3
4
5
6
7
8
9
10
11
12
13
package com.example.javaDesignPattern.simpleFactory;

/**
* @author 邓哈哈
* @version 1.0
* @date 2023/9/18 15:36
*/
public class ConcreteProductA implements Product {
@Override
public void execute() {
System.out.println("执行具体产品A的方法");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
package com.example.javaDesignPattern.simpleFactory;

/**
* @author 邓哈哈
* @version 1.0
* @date 2023/9/18 15:37
*/
public class ConcreteProductB implements Product {
@Override
public void execute() {
System.out.println("执行具体产品B的方法");
}
}
  • 定义一个工厂类,用来创建产品对象:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.example.javaDesignPattern.simpleFactory;

/**
* 工厂类
*
* @author 邓哈哈
* @version 1.0
* @date 2023/9/18 15:41
*/
public class Factory {
public static Product createProduct(String type) {
//根据传入的参数type的不同来创建不同的产品对象。
if (type.equals("A")) {
return new ConcreteProductA();
} else if (type.equals("B")) {
return new ConcreteProductB();
} else {
throw new IllegalArgumentException("Unknown product type: " + type);
}
}
}
  • 小小地测试一把:根据传入的参数,创建对应的产品实例,对外屏蔽了产品实例的创建过程:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.example.javaDesignPattern.simpleFactory;

/**
* 客户端
*
* @author 邓哈哈
* @version 1.0
* @date 2023/9/18 15:42
*/
public class Client {

public static void main(String[] args) {
Product productA = Factory.createProduct("A");
Product productB = Factory.createProduct("B");

productA.execute();
productB.execute();
}
}

工厂方法模式

  • 有了简单工厂,为什么还要有工厂方法呢?

  • 如上,简单工厂是根据传递的参数选择地创建不同的对象实例,即所有的对象实例是在同一个工厂中创建的

  • 如果需要创建的对象实例的类型有多种,我们不得不考虑为每一种对象实例定义一个专属的工厂,完成该对象实例的创建

  • 我们仍沿用上面的产品类,通过定义一个具体的工厂来创建该产品对象实例(2023/09/22 午)
  • 定义一个抽象工厂
1
2
3
4
5
6
7
8
9
10
/**
* 抽象工厂
*
* @author bug菌
* @version 1.0
* @date 2023/9/18 16:45
*/
public interface Creator {
Product createProduct();
}
  • 定义一个具体工厂
1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 具体工厂
*
* @author bug菌
* @version 1.0
* @date 2023/9/18 16:46
*/
public class ConcreteCreator implements Creator {
@Override
public Product createProduct() {
return new ConcreteProduct();
}
}
  • 小小地测试一把:使用具体工厂 ConcreteCreator,成功创建产品对象实例,并对外屏蔽对象的创建过程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 测试用例
*
* @author bug菌
* @version 1.0
* @date 2023/9/18 16:51
*/
public class Client {
public static void main(String[] args) {
Creator creator = new ConcreteCreator();
Product product = creator.createProduct();
product.doSomething();
}
}
  • 即,工厂方法与简单工厂的区别就是:工厂方法将工厂对对象实例的创建时机延迟到了实现该工厂的具体子类

抽象工厂模式

  • 设想一下,如果要做到在一个具体的工厂中,同时创建大量的互相关联的对象,该如何做到呢?

  • 答案就是在抽象工厂中,定义一组抽象的工厂方法,而具体工厂可以实现这些抽象方法,创建一系列相关的产品对象
  • 定义一个抽象工厂,定义一组抽象工厂方法:(2023/09/22 午)
1
2
3
4
5
6
7
8
9
10
11
12
13
package com.example.javaDesignPattern.abstractFactory;

/**
* 抽象工厂类
*
* @author bug菌
* @version 1.0
* @date 2023/9/18 17:29
*/
public abstract class AbstractFactory {
public abstract ProductA createProductA();
public abstract ProductB createProductB();
}
  • 具体工厂实现这些抽象方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 创建具体工厂类
*
* @author bug菌
* @version 1.0
* @date 2023/9/18 17:30
*/
public class ConcreteFactory1 extends AbstractFactory {
@Override
public ProductA createProductA() {
return new ConcreteProductA1();
}
@Override
public ProductB createProductB() {
return new ConcreteProductB1();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 创建具体工厂类
*
* @author bug菌
* @version 1.0
* @date 2023/9/18 17:30
*/
public class ConcreteFactory2 extends AbstractFactory {
@Override
public ProductA createProductA() {
return new ConcreteProductA2();
}
@Override
public ProductB createProductB() {
return new ConcreteProductB2();
}
}
  • 之后的思想就是定义产品抽象类,并定义具体的产品类,使用具体工厂来快速创建一系列相关的产品对象实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 创建客户端
*
* @author bug菌
* @version 1.0
* @date 2023/9/18 17:33
*/
public class Client {
public static void main(String[] args) {
AbstractFactory factory1 = new ConcreteFactory1();
AbstractFactory factory2 = new ConcreteFactory2();

ProductA productA1 = factory1.createProductA();
ProductB productB1 = factory1.createProductB();
productA1.display();
productB1.show();

ProductA productA2 = factory2.createProductA();
ProductB productB2 = factory2.createProductB();
productA2.display();
productB2.show();
}
}

策略模式

特点

  • 策略模式是一种行为型设计模式,它通过定义一组算法家族封装每个算法,并使它们可以相互替换,让算法的变换独立于使用算法的客户端

应用场景

  • 对象有多种行为或算法,需要根据不同的情况选择不同的算法(2023/10/20 早)
  • 系统中有多个类实现相同的接口或继承相同的抽象类,但具体实现不同
  • 需要在运行时动态地添加、删除或切换算法,而不影响客户端代码
  • 一个类有多种变形或状态,每个状态有不同的行为,需要根据状态动态地改变对象的行为

代码实现

  • 抽象策略
1
2
3
4
5
6
7
8
9
/**
* @author memory
* @version 1.0
* @date 2023/10/20 09:38
*/

public interface Strategy {
int doOperation(int num1, int num2);
}
  • 具体策略 A
1
2
3
4
5
6
7
8
9
10
11
/**
* @author memory
* @version 1.0
* @date 2023/10/20 09:38
*/

public class OperationAdd implements Strategy {
public int doOperation(int num1, int num2) {
return num1 + num2;
}
}
  • 具体策略 B
1
2
3
4
5
6
7
8
9
10
11
/**
* @author memory
* @version 1.0
* @date 2023/10/20 09:38
*/

public class OperationSubtract implements Strategy {
public int doOperation(int num1, int num2) {
return num1 - num2;
}
}
  • 使用环境(封装算法策略)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* @author memory
* @version 1.0
* @date 2023/10/20 09:38
*/

public class Context {
private Strategy strategy;

public Context(Strategy strategy) {
this.strategy = strategy;
}

public int executeStrategy(int num1, int num2) {
return strategy.doOperation(num1, num2);
}
}
  • 客户端调用测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* @author memory
* @version 1.0
* @date 2023/10/20 09:38
*/

public class StrategyTest {
public static void main(String[] args) {
Context context = new Context(new OperationAdd());
System.out.println("10 + 5 = " + context.executeStrategy(10, 5));

context = new Context(new OperationSubtract());
System.out.println("10 - 5 = " + context.executeStrategy(10, 5));
}
}

观察者模式

特点

  • 观察者模式是一种行为型设计模式,定义了一种一对多的依赖关系 (2023/10/20 早)
  • 多个观察者可以同时监听同一主题对象,对象主题发生改变时,会通知所有观察者对象,使他们能够及时做出响应
  • 观察者模式的核心是在主题对象和观察者对象之间建立一种松耦合的关系,以便于对象状态改变时通知观察者对象做出相应处理

代码实现

  • 主题接口
1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* @author memory
* @version 1.0
* @date 2023/10/20 09:46
*/

public interface Subject {
void attach(Observer observer);

void detach(Observer observer);

void notifyObservers();
}
  • 具体主题类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/**
* @author memory
* @version 1.0
* @date 2023/10/20 09:46
*/

class OrderSubject implements Subject {
private List<Observer> observers = new ArrayList<>();
private int state;

public int getState() {
return state;
}

public void setState(int state) {
this.state = state;
notifyObservers();
}

@Override
public void attach(Observer observer) {
observers.add(observer);
}

@Override
public void detach(Observer observer) {
observers.remove(observer);
}

@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(this);
}
}
}
  • 观察者接口
1
2
3
4
5
6
7
8
9
/**
* @author memory
* @version 1.0
* @date 2023/10/20 09:46
*/

public interface Observer {
void update(Subject subject);
}
  • 具体观察者
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* @author memory
* @version 1.0
* @date 2023/10/20 09:46
*/

public class OrderObserver implements Observer {
private int state;

@Override
public void update(Subject subject) {
state = ((OrderSubject) subject).getState();
System.out.println("订单状态发生变化,新状态为:" + state);
}

public int getState() {
return state;
}

public void setState(int state) {
this.state = state;
}
}
  • 客户端调用测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* @author memory
* @version 1.0
* @date 2023/10/20 09:46
*/

public class ObserverTest {
public static void main(String[] args) {
OrderSubject orderSubject = new OrderSubject();
OrderObserver orderObserver1 = new OrderObserver();
OrderObserver orderObserver2 = new OrderObserver();
orderSubject.attach(orderObserver1);
orderSubject.attach(orderObserver2);

orderSubject.setState(1);
orderSubject.detach(orderObserver1);
orderSubject.setState(0);
}
}

中介者模式

特点

  • 中介者模式是一种行为型设计模式,它主要用于将关系很复杂的对象之间的通信进行解耦,让这些对象通过一个中介对象进行通信
  • 降低对象之间的耦合度,使系统更加灵活和可扩展(2023/10/20 早)

代码示例

  • 中介者接口
1
2
3
4
5
6
7
8
9
/**
* @author memory
* @version 1.0
* @date 2023/10/20 09:55
*/

public interface Mediator {
void sendMessage(String message, User user);
}
  • 具体的中介者类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/**
* @author memory
* @version 1.0
* @date 2023/10/20 09:55
*/

public class ConcreteMediator implements Mediator {

// 所有的用户
private final Map<String, User> userMap;

public ConcreteMediator() {
this.userMap = new HashMap<>();
}

/**
* 注册用户
* @param user 待注册用户
*/
public void registerUser(User user) {
String userId = user.getUserId();
if (!userMap.containsKey(userId)) {
userMap.put(userId, user);
user.setMediator(this);
}
}

/**
* 发送消息给指定用户
* @param message 消息内容
* @param user 接收用户
*/
@Override
public void sendMessage(String message, Colleague user) {
String userId = user.getUserId();
if (userMap.containsKey(userId)) {
user.receiveMessage(message);
} else {
System.out.println("用户不存在");
}
}
}
  • 通信接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
* @author memory
* @version 1.0
* @date 2023/10/20 09:55
*/

public abstract class Colleague {

private final String userId;
private Mediator mediator;

public Colleague(String userId) {
this.userId = userId;
}

public String getUserId() {
return userId;
}

public Mediator getMediator() {
return mediator;
}

public void setMediator(Mediator mediator) {
this.mediator = mediator;
}

public void sendMessage(String message) {
mediator.sendMessage(message, this);
}

public abstract void receiveMessage(String message);
}
  • 具体的通信类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* @author memory
* @version 1.0
* @date 2023/10/20 09:55
*/

public class User extends Colleague {

public User(String userId) {
super(userId);
}

@Override
public void receiveMessage(String message) {
System.out.println("用户" + getUserId() + "收到消息:" + message);
}
}
  • 客户端调用测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
* @author memory
* @version 1.0
* @date 2023/10/20 09:55
*/

public class MediatorTest {

public static void main(String[] args) {
// 创建中介者
ConcreteMediator mediator = new ConcreteMediator();

// 创建用户
User user1 = new User("张三");
User user2 = new User("李四");
User user3 = new User("王五");

// 注册用户到中介者
mediator.registerUser(user1);
mediator.registerUser(user2);
mediator.registerUser(user3);

// 用户发送消息
user1.sendMessage("大家好,我是张三");
user2.sendMessage("你们好,我是李四");
user3.sendMessage("大家好,我是王五");
}
}

建造者模式

特点

  • 建造者模式是一种对象创建型设计模式,它将复杂对象的构建过程分离出来,使得同样的构建过程可以创建不同的表示
  • 该模式将构建对象的过程分为若干部分,分别进行构建,最终通过一个指挥者将这些部分组装成一个完整的对象
  • 解决复杂对象的创建和表示问题,减少构建过程中的重复代码,提高代码的重用性和可维护性(2023/10/20 早)

代码示例

  • 复杂的产品
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/**
* @author memory
* @version 1.0
* @date 2023/10/20 10:01
*/

public class Product {
private String partA;
private String partB;
private String partC;

public String getPartA() {
return partA;
}

public void setPartA(String partA) {
this.partA = partA;
}

public String getPartB() {
return partB;
}

public void setPartB(String partB) {
this.partB = partB;
}

public String getPartC() {
return partC;
}

public void setPartC(String partC) {
this.partC = partC;
}

public String toString() {
return "PartA: " + partA + ", PartB: " + partB + ", PartC: " + partC;
}
}
  • 抽象建造者
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* @author memory
* @version 1.0
* @date 2023/10/20 10:01
*/

public interface Builder {
void buildPartA();

void buildPartB();

void buildPartC();

Product getResult();
}
  • 具体建造者
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* @author memory
* @version 1.0
* @date 2023/10/20 10:01
*/

public class ConcreteBuilder implements Builder {
private Product product = new Product();

public void buildPartA() {
product.setPartA("A");
}

public void buildPartB() {
product.setPartB("B");
}

public void buildPartC() {
product.setPartC("C");
}

public Product getResult() {
return product;
}
}
  • 指挥者
1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* @author memory
* @version 1.0
* @date 2023/10/20 10:01
*/

public class Director {
public void construct(Builder builder) {
builder.buildPartA();
builder.buildPartB();
builder.buildPartC();
}
}
  • 客户端调用测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* @author memory
* @version 1.0
* @date 2023/10/20 10:01
*/

public class BuilderPattern {

public static void main(String[] args) {

Director director = new Director();
Builder builder = new ConcreteBuilder();
director.construct(builder);
Product product = builder.getResult();

System.out.println(product.getPartA());
System.out.println(product.getPartB());
System.out.println(product.getPartC());

System.out.println(product.toString());
}
}

原型模式

特点

  • 原型模式是一种通过复制现有对象来生成新对象的设计模式(2023/10/20 早)
  • 可以避免重复创建相似的对象,提高代码执行效率
  • 动态地生成对象,避免在代码中硬编码对象的创建过程
  • 可以实现对象的复用,减少对象的创建次数,降低系统开销

代码实现

  • 抽象原型类
1
2
3
4
5
6
7
8
9
/**
* @author memory
* @version 1.0
* @date 2023/10/20 10:07
*/

public abstract class Prototype implements Cloneable {
public abstract Prototype clone();
}
  • 具体原型类
1
2
3
4
5
6
7
8
9
10
11
12
/**
* @author memory
* @version 1.0
* @date 2023/10/20 10:07
*/

public class ConcretePrototype extends Prototype {
@Override
public Prototype clone() {
return new ConcretePrototype();
}
}
  • 客户端调用测试
1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* @author memory
* @version 1.0
* @date 2023/10/20 10:07
*/

public class Client {
public static void main(String[] args) {
Prototype prototype = new ConcretePrototype();
Prototype clone = prototype.clone();
System.out.println(clone);
}
}

命令模式

特点

  • 命令模式是一种行为设计模式,它将请求分装成一个对象
  • 命令模式的核心在于将请求和实现分离开,使请求具有独立的生命周期和实现 (2023/10/20 早)

代码实现

  • 抽象的命令接口
1
2
3
4
5
6
7
8
9
10
/**
* @author memory
* @version 1.0
* @date 2023/10/20 10:17
*/

public interface Command {
void execute();
void undo();
}
  • 具体命令
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* @author memory
* @version 1.0
* @date 2023/10/20 10:17
*/

public class ConcreteCommand implements Command {
private Receiver receiver;

public ConcreteCommand(Receiver receiver) {
this.receiver = receiver;
}

public void execute() {
receiver.action();
}

public void undo() {
receiver.undo();
}
}
  • 接收者
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* @author memory
* @version 1.0
* @date 2023/10/20 10:17
*/

public class Receiver {
public void action() {
System.out.println("执行命令");
}
public void undo() {
System.out.println("撤销命令");
}
}
  • 调用者
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* @author memory
* @version 1.0
* @date 2023/10/20 10:17
*/

public class Invoker {
private Command command;
public void setCommand(Command command) {
this.command = command;
}

public void executeCommand() {
command.execute();
}

public void undoCommand() {
command.undo();
}
}
  • 客户端调用测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* @author memory
* @version 1.0
* @date 2023/10/20 10:17
*/

public class Client {
public static void main(String[] args) {
Receiver receiver = new Receiver();
Command command = new ConcreteCommand(receiver);
Invoker invoker = new Invoker();
invoker.setCommand(command);
invoker.executeCommand();
invoker.undoCommand();
}
}

桥接模式

特点

  • 桥接模式是一种结构性设计模式,它将抽象和实现分开,以便它们可以独立地变化 (2023/10/20 早)
  • 在桥接模式中,有两个类层次结构:抽象类和实现类
  • 当你想要改变一个类的实现方式而不想影响到客户端代码 / 需要将一个抽象类和它的实现分开时,推荐使用桥接模式

代码实现

-

装饰器模式

特点

  • 装饰器模式是一种结构型设计模式,它允许我们在运行时扩展一个对象的功能,而不需要改变它的结构
  • 在装饰器模式中,我们创建一个装饰器类,该类是西安一个与被装饰对象相同的接口,并且通过将被装饰对象作为参数传递到装饰器中,实现对被装饰对象的包装。这使得我们可以在不修改原始对象的基础上,在运行时动态地添加或修改对象的功能
  • 装饰器模式常运用于:为对象添加缓存、日志记录、安全认证、性能优化等功能 (2023/10/20 午)

代码实现

  • 抽象组件
1
2
3
4
5
6
7
8
9
/**
* @author memory
* @version 1.0
* @date 2023/10/20 11:27
*/

public interface Component {
public String operation();
}
  • 具体组件(被装饰对象)
1
2
3
4
5
6
7
8
9
10
11
12
/**
* @author memory
* @version 1.0
* @date 2023/10/20 11:27
*/

public class ConcreteComponent implements Component {
@Override
public String operation() {
return "ConcreteComponent";
}
}
  • 抽象装饰器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* @author memory
* @version 1.0
* @date 2023/10/20 11:27
*/

public abstract class Decorator implements Component {
protected Component component;

public Decorator(Component component) {
this.component = component;
}

@Override
public String operation() {
return component.operation();
}
}
  • 具体装饰器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* @author memory
* @version 1.0
* @date 2023/10/20 11:27
*/

public class DecoratorA extends Decorator {
public DecoratorA(Component component) {
super(component);
}

@Override
public String operation() {
return "DecoratorA " + component.operation();
}
}
  • 客户端调用测试
1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* @author memory
* @version 1.0
* @date 2023/10/20 11:27
*/

public class DecoratorTest {
public static void main(String[] args) {
Component component = new ConcreteComponent();
Component decoratedComponent = new DecoratorA(component);
System.out.println(decoratedComponent.operation());
}
}

外观模式

特点

  • 外观模式是一种结构性设计模式 (2023/10/20 午)
  • 在外观模式中,通常有一个外观类,封装了子系统中的一组接口,客户端只需要调用外观类提供的统一接口,就能够完成其所需功能
  • 它隐藏了系统的复杂性,简化了客户端与子系统之间的交互,提高了系统的可维护性和可扩展性
  • 一个系统中有多个子系统,并且这些子系统之间互相合作以完成一个复杂的任务时,需要提供一个简单 de 接口来简化复杂的系统,推荐使用外观模式

代码示例

  • 子系统 A
1
2
3
4
5
6
7
8
9
10
11
12
/**
* @author memory
* @version 1.0
* @date 2023/10/20 14:04
*/

public class SubSystemA {
public void methodA() {
System.out.println("SubSystemA.methodA()");
}
}

  • 子系统 B
1
2
3
4
5
6
7
8
9
10
11
/**
* @author memory
* @version 1.0
* @date 2023/10/20 14:04
*/

public class SubSystemB {
public void methodB() {
System.out.println("SubSystemB.methodB()");
}
}
  • 子系统 C
1
2
3
4
5
6
7
8
9
10
11
/**
* @author memory
* @version 1.0
* @date 2023/10/20 14:04
*/

public class SubSystemC {
public void methodC() {
System.out.println("SubSystemC.methodC()");
}
}
  • 外观类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* @author memory
* @version 1.0
* @date 2023/10/20 14:04
*/

public class Facade {
private SubSystemA subSystemA;
private SubSystemB subSystemB;
private SubSystemC subSystemC;

public Facade() {
subSystemA = new SubSystemA();
subSystemB = new SubSystemB();
subSystemC = new SubSystemC();
}

public void method() {
subSystemA.methodA();
subSystemB.methodB();
subSystemC.methodC();
}
}
  • 客户端
1
2
3
4
5
6
7
8
9
10
11
12
/**
* @author memory
* @version 1.0
* @date 2023/10/20 14:04
*/

public class Client {
public static void main(String[] args) {
Facade facade = new Facade();
facade.method();
}
}

代理模式

特点

  • 代理模式是一种结构型设计模式,为其他对象提供一种代理,以控制对这些对象的访问,代理类与被代理类之间通过接口进行连接
  • 代理模式可隐藏客户端真正调用的对象实现代码解耦,增强系统的可维护性和可扩展性
  • 代理模式常用于需要控制对对象的访问,并提供远程访问、安全检查和缓存等功能 (2023/10/20 午)

代码示例

  • 抽象主题
1
2
3
4
5
6
7
8
9
/**
* @author memory
* @version 1.0
* @date 2023/10/20 14:12
*/

public interface Image {
void display();
}
  • 真实主题
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* @author memory
* @version 1.0
* @date 2023/10/20 14:12
*/

public class RealImage implements Image {
private String fileName;

public RealImage(String fileName) {
this.fileName = fileName;
loadFromDisk(fileName);
}

@Override
public void display() {
System.out.println("Displaying " + fileName);
}

private void loadFromDisk(String fileName) {
System.out.println("Loading " + fileName);
}
}
  • 抽象代理接口
1
2
3
4
5
6
7
8
9
/**
* @author memory
* @version 1.0
* @date 2023/10/20 14:12
*/

public interface ImageProxy extends Image {
void showImageInfo();
}
  • 真实代理类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
* @author memory
* @version 1.0
* @date 2023/10/20 14:12
*/

public class ProxyImage implements ImageProxy {
private String fileName;
private RealImage realImage;

public ProxyImage(String fileName) {
this.fileName = fileName;
}

@Override
public void display() {
if (realImage == null) {
realImage = new RealImage(fileName);
}
realImage.display();
}

@Override
public void showImageInfo() {
System.out.println("Image file: " + fileName);
}
}
  • 客户端调用测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* @author memory
* @version 1.0
* @date 2023/10/20 14:12
*/

public class ProxyPatternTest {

public static void main(String[] args) {
Image image = new ProxyImage("test.jpg");

// 代理对象和被代理对象都会调用display()方法
image.display();

ImageProxy imageProxy = new ProxyImage("test.jpg");

// 只有代理对象才有showImageInfo()方法
imageProxy.showImageInfo();
}
}

享元模式

特点

  • 享元模式是一种结构型设计模式,通过共享相同或相似的对象来减少内存消耗,提高系统的性能和效率 (2023/10/20 午)
  • 如果需要大量创建相同或相似对象,且创建和销毁对象的成本较高时,使用享元模式可以有效地优化系统性能和资源利用

代码示例

  • 抽象享元
1
2
3
4
5
6
7
8
9
/**
* @author memory
* @version 1.0
* @date 2023/10/20 14:21
*/

public interface Car {
void drive(String location);
}
  • 具体享元
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* @author memory
* @version 1.0
* @date 2023/10/20 14:21
*/

public class Bike implements Car {
private String type;

public Bike(String type) {
this.type = type;
}

@Override
public void drive(String location) {
System.out.println("Drive " + type + " bike to " + location);
}
}

  • 享元工厂
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* @author memory
* @version 1.0
* @date 2023/10/20 14:21
*/

public class CarFactory {
private static Map<String, Car> carPool = new HashMap<>();

public static Car getCar(String type) {
Car car = carPool.get(type);

if (car == null) {
car = new Bike(type);
carPool.put(type, car);
System.out.println("Create new car of type " + type);
}
return car;
}
}
  • 客户端调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* @author memory
* @version 1.0
* @date 2023/10/20 14:21
*/

public class Client {
public static void main(String[] args) {
Car car1 = CarFactory.getCar("Sport");
car1.drive("New York");

Car car2 = CarFactory.getCar("Sport");
car2.drive("San Francisco");

Car car3 = CarFactory.getCar("Economy");
car3.drive("Los Angeles");
}
}

责任链模式

特点

  • 责任链模式是一种行为型模式,它将多个对象连成一条链,并将请求沿着链传递,直到有一个对象处理它为止

  • 责任链模式将请求的发送者和接收者解耦,使得请求的发送者不需要知道请求的接收者是谁,也不需要知道请求是如何被处理的

  • 责任链模式适用于这样的情况: (2023/10/20 午)
    • 多个对象都有机会处理请求,但是不知道哪个对象最终会处理请求
    • 处理请求的对象集合可以动态配置,可以在运行时添加或删除处理对象
    • 发送者不需要知道请求的处理细节,只需要知道请求会被处理

代码示例

  • 抽象处理者
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* @author memory
* @version 1.0
* @date 2023/10/20 14:29
*/

public abstract class Handler {
protected Handler successor;

public void setSuccessor(Handler successor) {
this.successor = successor;
}

public abstract void handleRequest(String request);
}
  • 具体处理者 A
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* @author memory
* @version 1.0
* @date 2023/10/20 14:29
*/

public class ConcreteHandler1 extends Handler {
@Override
public void handleRequest(String request) {
if (request.equals("request1")) {
System.out.println("ConcreteHandler1 handles request: " + request);
} else {
if (successor != null) {
successor.handleRequest(request);
}
}
}
}
  • 具体处理者 B
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* @author memory
* @version 1.0
* @date 2023/10/20 14:29
*/

public class ConcreteHandler2 extends Handler {
@Override
public void handleRequest(String request) {
if (request.equals("request2")) {
System.out.println("ConcreteHandler2 handles request: " + request);
} else {
if (successor != null) {
successor.handleRequest(request);
}
}
}
}
  • 客户端调用测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* @author memory
* @version 1.0
* @date 2023/10/20 14:29
*/

public class Client {
public static void main(String[] args) {
Handler handler1 = new ConcreteHandler1();
Handler handler2 = new ConcreteHandler2();
handler1.setSuccessor(handler2);

handler1.handleRequest("request1");
handler1.handleRequest("request2");
}
}

状态模式

备忘录模式

迭代器模式

解释器模式

组合模式

模板方法模式

总结


改变编码游戏规则:揭秘23种设计模式的魅力和实用性
https://test.atomgit.net/blog/2023/09/17/改变编码游戏规则:揭秘23种设计模式的魅力和实用性/
作者
Memory
发布于
2023年9月17日
更新于
2023年10月20日
许可协议