设计模式是对大家实际工作中写的各种代码进行高层次抽象的总结,其中最出名的当属 Gang of Four (GoF) 的分类了,他们将设计模式分类为 23 种经典的模式,根据用途我们又可以分为三大类,分别为创建型模式、结构型模式和行为型模式。

参考文章:

设计模式——设计模式简介和七大原则

如何优雅的将设计模式运用到实际项目中去?

推荐文章:设计模式篇(2025版)

设计模式介绍

概述

  1. 设计模式是程序员在面对同类软件工程设计问题所总结出来的有用的经验。模式不是代码,而是某类问题的通用解决方案,设计模式(Design pattern)代表了最佳实践。

  2. 设计模式的本质提高软件的维护性、通用性和扩展性,并降低软件的复杂度

  3. Design Patterns - Elements of Reusable Object-Oriented Software》(设计模式 - 可复用的面向对象软件元素)是经典的书,该书首次提到了软件开发中设计模式的概念。作者是 Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides Design,合称 GOF(四人帮,全拼 Gang of Four)

  4. 设计模式并不局限于某种语言,Java、PHP、C++ 都有设计模式

设计模式目的及核心思想

重要性

  1. 设计模式是软件设计中普遍存在问题的解决方案。

  2. 便于项目开发完成后维护项目(可读性、规范性)、新增功能。

  3. 你在实际项目中使用过什么设计模式,怎么使用的?解决了什么问题?

  4. 项目的功能模块和框架里会使用设计模式。

设计模式目的

编写软件过程中,程序员面临着来自耦合性,内聚性以及可维护性,可扩展性,重用性,灵活性等多方面的挑战。

设计模式是为了让程序(软件),具有更好的

  1. 可复用性(即:相同功能的代码,不用多次编写,也叫做代码重用性)

  2. 可读性(即:编程规范性,便于其他程序员的阅读和理解)

  3. 可扩展性(即:当需要增加新的功能时,非常的方便,也叫做可维护性)

  4. 可靠性(即:当我们增加新的功能后,对原来的功能没有影响)

  5. 使程序呈现高内聚,低耦合的特性

设计原则核心思想

  • 1)找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起

  • 2)针对接口编程,而不是针对实现编程

  • 3)为了交互对象之间的松耦合设计而努力

设计模式分类(3类)

创建型模式(对象创建优化)

创建型模式(Creational Patterns):主要用于对象的创建,包括多个不同的模式,如工厂方法模式、抽象工厂模式、建造者模式、单例模式和原型模式等。这些模式都有助于降低系统耦合度,并提高代码的可重用性和可扩展性。

  1. 单例模式
    • 作用:确保类仅有一个实例(如数据库连接池),全局共享访问点。
    • 实现:双重检查锁(volatile + synchronized)或静态内部类。
  2. 工厂模式
    • 简单工厂:通过参数类型创建对象,将对象创建延迟到子类(如支付方式:支付宝/微信)。
    • 工厂方法:子类决定实例化逻辑(扩展性更强)。
    • 抽象工厂:创建相关对象族(如跨平台UI组件)。

单例模式(Singleton Pattern)、工厂模式(Factory Pattern)、抽象工厂模式(Abstract Factory Pattern)

建造者模式(Builder Pattern)、原型模式(Prototype Pattern)

结构型模式(类/对象组合优化)

结构型模式(Structural Patterns):主要用于描述对象之间的组合关系,包括多个不同的模式,如“代理模式”、“适配器模式”、“桥接模式”、“装饰者模式”、“外观模式”、“享元模式”和“组合模式”等。这些模式可以帮助我们更好地设计程序结构,提高代码灵活性和可维护性。

  1. 适配器模式
    • 场景:兼容接口差异(如老式打印机接入新系统);
    • 类型:类适配器(继承)或对象适配器(组合)。
  2. 装饰器模式
    • 动态扩展功能:替代继承避免类爆炸(如Java I/O流嵌套包装)。
  3. 代理模式
    • 控制访问:远程调用(RPC)、权限校验(Spring AOP切面)。
  4. 外观模式
    • 提供一个统一的接口,用来访问子系统中的一群接口,外观定义了一个高层的接口,让子系统更容易使用。

适配器模式(Adapter Pattern)、装饰器模式(Decorator Pattern)、代理模式(Proxy Pattern)
外观模式(Facade Pattern)

桥接模式(Bridge Pattern)、过滤器模式(Filter、Criteria Pattern)、组合模式(Composite Pattern)
享元模式(Flyweight Pattern)

行为型模式(对象交互优化)

行为型模式(Behavioral Patterns):主要用于描述对象之间的通信和责任分配,包括多个不同的模式,如“策略模式”、“模板方法模式”、“观察者模式”、“迭代器模式”、“职责链模式”、“命令模式”、“访问者模式”、“备忘录模式”和“解释器模式”等。这些模式通常用于实现不同的算法、流程和通信方式,以实现系统的更高灵活性和可维护性。

  1. 策略模式

    • 算法封装:电商促销策略(满减/折扣)动态切换;
    • 实现Comparator接口实现自定义排序。
  2. 观察者模式

    • 事件驱动:定义了对象之间的一对多的依赖,一对多状态通知(如配置中心变更推送);
    • 工具java.util.Observable
  3. 责任链模式

    • 流程解耦:多级审批系统或过滤器链(如Servlet Filter)。
  4. 模版方法模式(Template Method)

    • 定义了一个算法的骨架,子类重写特定步骤中,模版方法使得子类可在不改变算法结构的情况下,重新定义算法的步骤,(如JdbcTemplate执行流程固定)。
  5. 状态模式(State)

    • 对象行为随内部状态改变(如订单状态机:待支付→已发货)‌,允许对象在内部状态改变时改变它的行为

策略模式(Strategy Pattern)、观察者模式(Observer Pattern)、责任链模式(Chain of Responsibility Pattern)
模板模式(Template Pattern)、状态模式(State Pattern)

命令模式(Command Pattern)、解释器模式(Interpreter Pattern)、迭代器模式(Iterator Pattern)
中介者模式(Mediator Pattern)、备忘录模式(Memento Pattern)、空对象模式(Null Object Pattern)
访问者模式(Visitor Pattern)

J2EE 模式

这些设计模式特别关注表示层。这些模式是由 Sun Java Center 鉴定的。

  • MVC 模式(MVC Pattern)
  • 业务代表模式(Business Delegate Pattern)
  • 组合实体模式(Composite Entity Pattern)
  • 数据访问对象模式(Data Access Object Pattern)
  • 前端控制器模式(Front Controller Pattern)
  • 拦截过滤器模式(Intercepting Filter Pattern)
  • 服务定位器模式(Service Locator Pattern)
  • 传输对象模式(Transfer Object Pattern)

关键应用对照表

模式 典型应用场景 代表框架实现
单例 配置管理器、线程池 Spring Bean默认作用域
工厂方法 Spring的FactoryBean接口 BeanFactory实例化对象
代理 MyBatis的Mapper接口动态代理 JDK动态代理生成DAO对象
观察者 Spring的事件监听机制(ApplicationEvent 配置更新广播

模式选择原则

  • 创建对象复杂 → 工厂模式
  • 扩展对象功能 → 装饰器/代理
  • 算法灵活切换 → 策略模式
  • 状态联动更新 → 观察者模式‌

三种工厂模式的应用场景

模式 适用场景
简单工厂 产品种类少且不常变化,客户端只需传入参数,无需关心创建逻辑。
工厂方法 需要灵活扩展新产品,且不同子类可能需要不同的创建逻辑。
抽象工厂 需要创建一组相关或依赖的对象(产品族),且系统需要支持多套产品配置。

设计模式遵循的原则

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

    对类来说,即一个类应该只负责一项职责。对接口来说,接口设计要符合单一职责原则,粒度越小通用性就越好。

  2. 开闭原则(Open Close Principle)

    对扩展开放,对修改关闭

  3. 里氏代换原则(Liskov Substitution Principle)

    只有当衍生类可替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。

  4. 依赖倒转原则(Dependence Inversion Principle)

    这个是开闭原则的基础,对接口编程,依赖于抽象而不依赖于具体

  5. 接口隔离原则(Interface Segregation Principle)

    使用多个隔离的接口来降低耦合度。

  6. 迪米特法则(最少知道原则)(Demeter Principle)

    一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。

  7. 合成复用原则(Composite Reuse Principle)

    原则是尽量使用合成/聚合的方式,而不是使用继承继承实际上破坏了类的封装性,超类的方法可能会被子类修改。


设计模式简单实现模板

单例模式

应用场景

‌单例模式的核心应用场景包括资源管理、配置管理、日志记录、数据库连接池等需要全局唯一实例的场景‌。该模式通过确保类仅有一个实例并提供全局访问点,优化系统性能和资源利用率。‌‌‌‌

核心应用场景

  1. 资源管理优化
    适用于需频繁创建和销毁的高成本资源(如数据库连接、线程池、硬件外设管理),通过共享唯一实例减少内存开销和性能损耗。‌‌‌‌

    • 数据库连接池:避免重复建立连接,提升数据库操作效率
    • 硬件外设管理:如SPI Flash、ADC模块的全局访问控制
  2. 全局状态一致性
    确保多线程或跨模块访问时数据同步,例如:

    • 配置管理器:统一管理应用程序配置参数,防止状态不一致
    • 日志记录器:集中处理日志输出,便于跟踪和分析
  3. 性能敏感场景

    • 缓存系统:全局唯一的缓存实例可避免重复加载数据
    • 游戏引擎核心组件:如资源管理器、纹理加载器等
  1. 其他典型场景

    • 操作系统服务‌:如任务管理器、回收站等需唯一实例的系统组件

    • ID生成器/计数器‌:生成唯一序列时防止多实例导致冲突

    • 跨进程共享对象‌:如.NET框架中的远程单例对象

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
// 懒汉式(静态内部类)
class Singleton {
private Singleton() {}

// 写一个静态内部类,该类中有一个静态属性Singleton
private static class SingletonInstance {
private static final Singleton INSTANCE = new Singleton();
}

public static synchronized Singleton getInstance() {
return SingletonInstance.INSTANCE;
}
}

简单工厂模式

通过一个工厂类,根据传入的参数动态决定创建哪种具体产品类的实例。
核心:将对象的创建逻辑集中在一个类中

应用场景

产品种类较少且固定;客户端不需要关心对象创建细节。

代码实现

场景:用户支付场景,目前支持支付宝支付和微信支付,未来会新增银行卡,云闪付等方式。使用策略模式,每一种支付方式都是一种策略,根据用户传入的支付类型,创建不同的策略类,使用工厂模式,通过封装一个PaymentStrategyHandler策略处理类,其他系统直接通过一个统一的入口,进行该功能的调用,使用门面模式。

  1. 定义一个策略类

    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
    public interface IPayment {
        Boolean pay(PaymentBody paymentBody); // 支付
    }

    public class AliPay implements IPayment {
        @Override
        public Boolean pay(PaymentBody paymentBody) {
            System.out.println("支付宝支付...");
            return Boolean.TRUE;
        }
    }

    public class WechatPay implements IPayment {
        @Override
        public Boolean pay(PaymentBody paymentBody) {
            System.out.println("微信支付...");
            return Boolean.TRUE;
        }
    }

    public class UnionPay implements IPayment {
        @Override
        public Boolean pay(PaymentBody paymentBody) {
            System.out.println("银联支付...");
            return Boolean.TRUE;
        }
    }
  2. 创建策略工厂类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    import cn.hutool.core.util.EnumUtil;
    import cn.hutool.core.util.ReflectUtil;
    import com.universal.core.designPatterns.enums.PayStrategyEnum;
    import org.springframework.stereotype.Component;

    // Factory for payment methods
    @Component
    public class PaymentFactory {
        public static IPayment getPayStrategy(String type) {
            // 1.通过枚举中的type获取对应的value
            String value = EnumUtil.getFieldBy(PayStrategyEnum::getValue, PayStrategyEnum::getType, type);
            // 2.使用反射机制创建对应的策略类
            IPayment payment = ReflectUtil.newInstance(value);
            return payment;
        }
    }
  3. 定义策略枚举

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // 支付策略枚举
    @Getter
    public enum PayStrategyEnum {
        ZFB("ZFB""com.universal.core.designPatterns.factory.impl.AliPay"),
        WX("WX""com.universal.core.designPatterns.factory.impl.WechatPay"),
        UNION("UNION""com.universal.core.designPatterns.factory.impl.UnionPay");
        String type;
        String value;

        PayStrategyEnum(String type, String value) {
            this.type = type;
            this.value = value;
        }
    }
  4. 创建策略的上下文角色

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class PaymentContext {
        @Resource
        private IPayment paymentStrategy;

        public PaymentContext(IPayment paymentStrategy) {
            this.paymentStrategy = paymentStrategy;
        }
        public Boolean pay(PaymentBody paymentBody) {
            return this.paymentStrategy.pay(paymentBody);
        }
    }
  5. 提供统一访问处理入口

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    import cn.hutool.core.util.EnumUtil;
    import com.universal.core.designPatterns.enums.PayStrategyEnum;
    import org.springframework.stereotype.Component;

    @Component
    public class PaymentStrategyHandler {
    public static Boolean pay(PaymentBody payBody) {
    if (!EnumUtil.contains(PayStrategyEnum.class, payBody.getType())) {
    throw new IllegalArgumentException("不支持的支付方式!");
    }
    // 1.获取支付策略对象
    IPayment payStrategy = PaymentFactory.getPayStrategy(payBody.getType());
    // 2.获取支付策略上下文
    PaymentContext paymentContext = new PaymentContext(payStrategy);
    // 3.进行支付
    return paymentContext.pay(payBody);
    }
    }
  6. 创建Controller

    1
    2
    3
    4
    5
    6
    7
    8
    @RestController
    @RequestMapping(value = "/designPatterns")
    public class DesignPatternController {
    @PostMapping("/pay")
    public Boolean pay(@RequestBody PaymentBody paymentBody){
    return PaymentStrategyHandler.pay(paymentBody);
    }
    }

工厂方法模式

与简单工厂的区别

简单工厂模式的最大优点在于包含了必要的逻辑判断,根据客户端的选择条件动态实例化相关的类,相对于客户端来说,去除了与具体产品的依赖。

  •  简单工厂:用来生产同一等级结构中的任意产品,对于增加新的产品,无能为力。
  •  工厂方法:用来生产同一等级结构中的固定产品,支持增加任意产品。

应用场景

产品种类可能动态扩展;需要将对象创建逻辑分散到不同子类中

代码实现

工厂方法模式(Factory Method),定义一个用于创建对象的接口,让子类决定实例化哪一个类,工厂方法是一个类的实例化延迟到其子类,通俗来说:它提供了一种实例化逻辑委托子类的方法。代码示例:

  1. 定义NetworkConfigFactoryService工厂类

    1
    2
    3
    4
    5
    6
    7
    import com.universal.core.designPatterns.factoryMethod.NetworkConfigCrudService;

    public interface NetworkConfigFactoryService {
        // 获取指定的处理逻辑类
        // @param productType
        NetworkConfigCrudService getSpecificService(String productType);
    }
  2. NetworkConfigFactoryService工厂实现类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    @Service
    public class NetworkConfigFactoryServiceImpl implements NetworkConfigFactoryService {

    private final AServiceImpl aService;
    private final BServiceImpl bService;
    private final CServiceImpl cService;
    private final DServiceImpl dService;

    @Override
    public NetworkConfigCrudService getSpecificService(String productType) {
    NetworkConfigCrudService networkConfigCrudService = null;
    switch (productType){
    case "A": networkConfigCrudService = aService; break;
    case "B": networkConfigCrudService = bService; break;
    case "C": networkConfigCrudService = cService; break;
    case "D": networkConfigCrudService = dService; break;
    }
    return networkConfigCrudService;
    }
    }
  3. 定义网点操作接口NetworkConfigCrudService

    1
    2
    3
    public interface NetworkConfigCrudService {
    NetworkConfigVO getNetwork(NetworkConfigDTO networkConfigDTO);
    }

    它的实现类分别是 AServiceImplBServiceImplCServiceImplDServiceImpl,分别对应不同的逻辑:

    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
    @Service
    public class AServiceImpl implements NetworkConfigCrudService {
    @Override
    public NetworkConfigVO getNetwork(NetworkConfigDTO networkConfigDTO) {
    return new NetworkConfigVO();
    }
    }

    @Service
    public class BServiceImpl implements NetworkConfigCrudService {
    @Override
    public NetworkConfigVO getNetwork(NetworkConfigDTO networkConfigDTO) {
    return new NetworkConfigVO();
    }
    }
    @Service
    public class CServiceImpl implements NetworkConfigCrudService {
    @Override
    public NetworkConfigVO getNetwork(NetworkConfigDTO networkConfigDTO) {
    return new NetworkConfigVO();
    }
    }
    @Service
    public class DServiceImpl implements NetworkConfigCrudService {
    @Override
    public NetworkConfigVO getNetwork(NetworkConfigDTO networkConfigDTO) {
    return new NetworkConfigVO();
    }
    }
  4. 控制层NetworkConfigController

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    @Slf4j
    @RestController
    @RequestMapping(value = "/networkConfig")
    public class NetworkConfigController {
    private final NetworkConfigFactoryService factoryService;

    @PostMapping(value = "/getNetworkDetails", produces = MediaType.APPLICATION_JSON_VALUE)
    public ApiResult<NetworkConfigVO> getNetworkDetails(@RequestBody NetworkConfigDTO networkConfigDTO) {
    // 获取AService处理类逻辑
    NetworkConfigCrudService aService = factoryService.getSpecificService("A");
    NetworkConfigVO network = aService.getNetwork(networkConfigDTO);
    return ApiResult.success(network);
    }
    }

抽象工厂模式

定义 提供一个接口,用于创建相关或依赖对象家族,而无需指定具体类。
核心:生产多个产品族(如不同操作系统下的UI组件)

应用场景

需要创建多个相互关联的产品(如不同主题的UI组件、跨平台适配);系统需要独立于产品的创建和组合过程

代码实现

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
// 1. 定义产品族接口
interface Button {
void render();
}
interface TextField {
void display();
}

// 2. 具体产品族实现(Windows风格)
class WindowsButton implements Button {
@Override
public void render() {
System.out.println("Windows风格按钮");
}
}
class WindowsTextField implements TextField {
@Override
public void display() {
System.out.println("Windows风格文本框");
}
}

// 3. 具体产品族实现(MacOS风格)
class MacOSButton implements Button {
@Override
public void render() {
System.out.println("MacOS风格按钮");
}
}
class MacOSTextField implements TextField {
@Override
public void display() {
System.out.println("MacOS风格文本框");
}
}

// 4. 抽象工厂接口
interface GUIFactory {
Button createButton();
TextField createTextField();
}

// 5. 具体工厂实现-windows
class WindowsFactory implements GUIFactory {
@Override
public Button createButton() {
return new WindowsButton();
}
@Override
public TextField createTextField() {
return new WindowsTextField();
}
}
// 5. 具体工厂实现(MacOS风格)
class MacOSFactory implements GUIFactory {
@Override
public Button createButton() {
return new MacOSButton();
}
@Override
public TextField createTextField() {
return new MacOSTextField();
}
}

// 6. 客户端调用
public class Client {
public static void main(String[] args) {
GUIFactory factory = new MacOSFactory();
Button button = factory.createButton();
TextField textField = factory.createTextField();
button.render(); // 输出:MacOS风格按钮
textField.display(); // 输出:MacOS风格文本框
}
}

适配器模式

适配器模式Adapter 是将一个接口转换成另一个客户所期望的接口。**Adapter** 适配器让那些本来因为接口不兼容的类可以合作无间。

Spring 框架中适配器模式的五大典型应用场景

适配器模式的角色分析

  • 目标接口(Traget):客户期待的接口,目标可以是具体的或者抽象的类,也可以是接口。
  • 需要适配的对象(Source Adaptee):需要适配的对象。
  • 适配器(Adapter):通过包装一个需要适配的对象,把原接口转成目标接口。

应用场景

Java适配器模式在实际开发中主要应用于系统整合、接口兼容和功能扩展三大场景,通过对象适配器和类适配器两种方式实现接口转换。

典型应用场景

  1. 系统整合场景。 将旧系统的接口适配到新系统(如日志框架转换)。 示例

    • 通过FileLoggerAdapter将基于文件的日志写入适配到数据库日志接口。
    • 企业级应用:整合遗留系统接口与企业中台规范。
  2. 接口兼容场景。 多线程开发中将Callable适配为Runnable接口。 示例

    • RunnableAdapter转发Callable任务给Thread执行。
    • JDK应用InputStreamReader将字节流适配为字符流。
  3. 功能扩展场景。 多媒体播放器扩展播放格式。 示例

    • MediaPlayer适配AdvancedMediaPlayer实现mp4/vlc播放支持
    • Android开发BaseAdapter统一ListView与不同类型数据源的交互。

实现方式对比

  1. 对象适配器(推荐)。 通过组合持有被适配对象实例。 优势:支持多态、适配器可复用、避免单继承限制。
  2. 类适配器。 通过继承实现接口转换。 局限:单继承限制、无法适配多个类。 示例
    • ExpensiveAdapter继承HuaweiPhone实现Player接口。

最佳实践原则

  1. 接口设计规范。 目标接口保持最小化定义。 适配器类命名遵循XxxAdapter格式。
  2. 组合优于继承。 对象适配器比例达85%以上(Java设计规范推荐)。组合方式降低耦合度,支持动态替换适配对象。
  3. 异常处理机制。 在适配器中处理被适配对象的异常。 示例:
    • RunnableAdapter捕获Callable异常并转换为运行时异常。

类适配器代码实现

场景:以网线上网为例,现在有一根水晶头网线,但它的接口与电脑的不匹配(因为电脑的是usb或者typec),那就需要一个转接头,也就是适配器,才能上网,下面的转接头可理解为适配器:

  1. 我们拥有一根网线, 其有上网的功能,但是它的接口与电脑不匹配

    1
    2
    3
    4
    5
    6
    // 要适配的类:网线
    public class Adaptee {
    public void request() { // 功能:上网
    System.out.println("链接网线上网");
    }
    }
  2. 定义一个usb接口,也就是目标接口(Target)

    1
    2
    3
    4
    // 接口转换器的抽象实现
    public interface NetToUsb {
    public void handleRequest(); // 作用:处理请求,网线 => usb
    }
  3. 定义一个适配器继承着网线,连接着usb接口

    1
    2
    3
    4
    5
    6
    7
    // 真正的适配器,余姚链接usb,连接网线
    public class Adapter extends Adaptee implements NetToUsb {
    @Override
    public void handleRequest() {
    super.request(); // 可以上网了
    }
    }
  4. 上网的具体实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // 客户端类:想上网,插不上网线
    public class Computer {
    // 电脑需要连接上转接器才可以上网
    public void net(NetToUsb adapter){
    adapter.handleRequest(); // 上网的具体实现:找一个转接头
    }
    public static void main(String[] args) {
    // 电脑,适配器,网线
    Computer computer = new Computer(); // 电脑
    Adapter adapter = new Adapter(); // 转接器
    computer.net(adapter); // 电脑直接连接转接器就可以
    }
    }

对象适配器应用

  • java.util.Arrays#asList()open in new window
  • java.util.Collections#list()open in new window
  • java.util.Collections#enumeration()open in new window
  • javax.xml.bind.annotation.adapters.XMLAdapter

代理模式

**代理模式Proxy**,为其他对象提供一种代理以控制对这个对象的访问。

应用场景

‌代理模式的核心使用场景包括访问控制、功能扩展和对象隔离三大类‌,具体可分为远程访问优化、资源延迟加载、权限管理、性能增强等典型应用场景。‌

远程服务访问

  1. 远程代理‌:解决跨网络对象调用问题,客户端通过本地代理对象访问远程服务端资源。典型应用包括:

    • 分布式系统中的RPC调用(如Dubbo框架)
    • 数据库连接池管理
    • CORBA远程对象调用

资源访问控制

  1. 保护代理‌:通过权限过滤实现访问控制,包含两种实现维度:

    • 身份认证代理:控制特定用户/角色的操作权限(如OA系统审批接口代理)

    • 防火墙代理:过滤非法请求(如Web应用防火墙)

  2. 虚拟代理‌:优化高开销对象的使用效率:

    • 大型文件加载时显示缩略图(如图库管理系统)
    • 延迟加载数据库连接(如MyBatis延迟加载)
    • 视频流分片加载(如在线视频网站)

系统功能增强

  1. 智能代理‌:在方法调用前后插入扩展逻辑:

    • 日志记录代理(记录API调用信息)
  • 事务管理代理(Spring声明式事务)
    • 缓存代理(MyBatis二级缓存机制)
  1. 适配代理‌:解决接口兼容性问题:

    • API版本适配(如新旧支付接口兼容)
    • 协议转换(如HTTPWebSocket
    • 数据格式转换(XMLJSON互转)

代码实现

代理模式 实际上在平时中也运用的非常广泛,最经典的例子就是房东委托中介代理出租房子的案例。

  1. 创建一个Subject类

    1
    2
    3
    4
    // 活动类,目的是出租房子
    public interface Subject {
        void rentHouse(); // 租房接口
    }
  2. 定义一个房东角色,现在活动类

    1
    2
    3
    4
    5
    6
    7
    8
    // 房东
    public class HouseOwner implements Subject {
    // 实现租房方法
    @Override
    public void rentHouse() {
    System.out.println("房东成功出租了房子...");
    }
    }
  3. 定义一个中介代理对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 中介代理类
    // 一般情况下我们不能直接联系到房东,所以需要提供一个代理类,即中介类
    public class HouseProxy implements Subject {
        private HouseOwner houseOwner = new HouseOwner();

        @Override
        public void rentHouse() {
            System.out.println("中介收取代理费,帮助房东出租房子...");
            houseOwner.rentHouse();
        }
    }
  4. 模拟用户找中介租房子

    1
    2
    3
    4
    public static void main(string[] args){
    HouseProxy proxy = new HouseProxy();
    proxy.rentHouse();
    }

策略模式

应用场景

策略模式的核心应用场景聚焦于需要动态选择算法或业务规则的场景‌,主要适用于算法切换、支付方式选择、会员折扣系统、订单处理逻辑和游戏AI行为等需要灵活扩展的业务场景。‌‌

策略模式的典型应用场景‌:策略模式通过封装算法族并实现运行时动态切换,有效解决了复杂条件判断和频繁扩展需求的问题‌。以下是其核心应用领域:

  1. 算法动态切换场景

    排序算法选择(快速排序、归并排序)。‌‌

    文件压缩格式切换(ZIPRAR7z)。‌‌

    加密算法适配(AESRSAMD5)。‌‌

    日志输出方式配置(文件存储、网络传输、控制台输出)。‌‌

  2. 电商领域高频应用

    支付方式选择(信用卡、支付宝、微信支付),通过策略接口统一调用不同支付通道。‌‌‌‌

    折扣策略配置(满减、折扣券、会员价),支持促销活动的快速迭代。‌‌

    订单类型处理(普通订单、团购订单、促销订单),替代多层级if-else判断逻辑。‌‌

  3. 系统行为动态调整

    游戏AI策略切换(进攻型、防御型、逃跑型行为模式)。‌‌

    交通出行方式计算(飞机、火车、自驾的成本与时间策略)。‌‌

    影院售票系统(学生折扣、儿童减免、VIP积分等差异化策略)。‌‌

  4. 策略模式的使用优势

    扩展性‌:新增策略只需实现接口,无需修改上下文逻辑。‌‌

    解耦性‌:算法实现与使用逻辑分离,符合开闭原则。‌‌

    可维护性‌:消除大规模条件判断,代码结构更清晰。‌‌

基本实现步骤

场景: 商场搞活动,根据客户购买商品的金额,收费时给与不同的打折,比如,购买 金额>=2000 的打八折(0.8),金额 500 ~ 1000 的,打九折(0.9),购买金额 0 ~ 500 的九五折(0.95),根据不同的金额走不同计算策略逻辑。

  1. 定义一个Strategy接口来表示一个策略

    其中strategy方法返回当前策略的唯一标识,algorithm则是该策略的具体执行的计算逻辑。代码如下:

    1
    2
    3
    4
    public interface Strategy {
        String strategy(); // 采用策略
        void algorithm(); // 计算方法逻辑
    }
  2. 定义几个是Strategy接口的实现类:

    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
    public class ConcreteStrategyA implements Strategy {
        @Override
        public String strategy() {
            return StrategySelector.strategyA.getStrategy();
        }
        @Override
        public void algorithm() {
            System.out.println("process with strategyA...");
        }
    }

    public class ConcreteStrategyB implements Strategy {
        @Override
        public String strategy() {
            return StrategySelector.strategyB.getStrategy();
        }

        @Override
        public void algorithm() {
            System.out.println("process with strategyB...");
        }
    }

    public class ConcreteStrategyC implements Strategy {
        @Override
        public String strategy() {
            return StrategySelector.strategyC.getStrategy();
        }

        @Override
        public void algorithm() {
            System.out.println("process with strategyC...");
        }
    }
  3. 自定义策略选择枚举 StrategySelector

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    @Getter
    public enum StrategySelector {
        strategyA(1,"strategyA"),
        strategyB(2,"strategyB"),
        strategyC(3,"strategyC");
        
        private Integer code;
        private String strategy;

        StrategySelector(Integer code, String strategy) {
            this.code = code;
            this.strategy = strategy;
        }
    }
  4. 定义一个StrategyRunner接口用来表示策略的调度器

    1
    2
    3
    public interface StrategyRunner {
    void execute(String strategy);
    }
  5. execute方法内部通过判断strategy的值来决定具体执行哪一个策略

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public class StrategyRunnerImpl implements StrategyRunner {
        private static final List<Strategy> STRATEGIES = 
    Arrays.asList(new ConcreteStrategyA(), new ConcreteStrategyB(), new ConcreteStrategyC());
        private static Map<String, Strategy> STRATEGY_MAP = Maps.newHashMap();

        static {
            STRATEGY_MAP = STRATEGIES.stream().collect(Collectors.toMap(Strategy::strategy, s -> s));
        }

        @Override
        public void execute(String strategy) {
            STRATEGY_MAP.get(strategy).algorithm();
        }
    }

    StrategyRunnerImpl内部,定义了一个STRATEGIES列表来保存所有Strategy实现类的实例,以及一个叫做STRATEGY_MAP的Map来保存strategyStrategy实例之间的对应关系,static块中的代码用于从STRATEGIES列表构造STRATEGY_MAP。这样,在execute方法中就可以很方便地获取到指定 strategy 的 Strategy 实例。

实现并运用策略模式

  1. 定义一个Strategy接口来表示一个策略,再定义几个是Strategy接口的实现类,同上【1 - 2】步骤,注意需要在自定义Strategy实现类上标注@Component注解以将其注册进容器。

  2. 定义一个StrategyConfig配置类,用于向容器注入一个StrategyRunner

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @Configuration
    public class StrategyConfig {
        @Bean
        public StrategyRunner runner(List<Strategy> strategies) {
            Map<String, Strategy> strategyMap = 
    strategies.stream().collect(Collectors.toMap(Strategy::strategy, s -> s));
            return flag -> strategyMap.get(flag).algorithm();
        }
    }

    strategyRunner方法的实现,逻辑与StrategyRunnerImpl几乎完全相同,也是根据一个List<Strategy>来构造一个Map<String, Strategy>。不过,这里的strategies列表是通过方法参数传进来的。由于strategyRunner标注了Bean注解,因此参数上的List<Strategy>实际上是在Spring Boot初始化过程中从容器获取的,所以我们之前向容器中注册的那两个实现类会在这里被注入。 这样就不必关注系统中Strategy实现类的具体个数,因为Spring Boot的自动配置会帮我们自动发现所有实现类。

  3. 在任何需要的地方注入StrategyRunner,直接使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @RestController
    @RequestMapping(value = "/designPatterns")
    public class DesignPatternController {

    @Autowired
    private StrategyRunner strategyRunner;

    @GetMapping(value = "/algorithm")
    public void algorithm(@RequestParam("strategy") String strategy) {
    strategyRunner.execute(strategy);
    }
    }
  4. 访问接口,控制台输出如下

    1
    process with strategyA...

观察者模式

观察者模式Observer 定义了对象之间的一对多依赖,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。

初识观察者模式:报社+订阅者 = 观察者模式。

要点

  • 观察者模式定义了对象之间一对多的关系。
  • 主题(可观察者)用一个共同的接口来更新观察者。
  • 观察者和可观察者之间用松耦合方式结合,可观察者不知道观察者的细节,只知道观察者实现了观察者接口。
  • 使用次模式时,你可以从被观察者处推或拉数据(推的方式被认为是更正确的)。
  • 有多个观察者时,不可以依赖特定的通知次序。
  • java中有多种观察者模式的实现,包括了通用的java.util.Observable,不过需要注意Observable实现上所带来的问题,有必要的话,可以实现自己的Observable
  • Spring也大量使用观察者模,比如ListenrEvent消息订阅与发布;

应用场景

  1. GUI 事件处理
    场景:用户界面组件(如按钮、输入框)的状态变化需要触发多个事件监听器
    案例

    • 点击按钮后,触发日志记录、界面更新、数据提交等多个操作

    • 输入框内容变化时,实时校验输入合法性并更新提示信息

    • 框架应用:Java Swing、Android 的 OnClickListener、JavaScript 的addEventListener

  2. 实时数据同步
    场景:数据源的变更需要实时同步到多个客户端或组件。案例

    • 股票行情系统:股价变动时,所有关注的投资者界面自动刷新
    • 在线协作工具(如 Google Docs):一个用户编辑内容,其他用户的视图实时更新
    • 前端框架(如 Vue、React)的数据绑定:数据变化驱动视图渲染
    • 一个主界面由几个子界面垂直布局组成, 数据源变更,子界面数据将实时变化(页面还有几个一级标题页面,为了解耦和代码管理按照标题差分类结构)
  3. 状态监控与报警
    场景:监控系统状态变化,并触发相关响应(如日志、报警、资源调整)。案例

    • 服务器 CPU 使用率超过阈值时,触发邮件报警、记录日志、自动扩容

    • 物联网设备(如传感器)数据异常时,通知用户和管理系统

  4. 游戏开发中的事件系统
    场景:游戏内事件(如角色死亡、任务完成)需要触发多模块响应。案例

    • 玩家生命值降为 0 时,触发 UI 更新死亡动画、保存进度、播放音效

    • 成就系统:当玩家达成特定条件(如击杀 100 个敌人),解锁成就并推送通知

  5. 配置或参数动态更新
    场景:系统配置变更后,相关组件需动态调整行为,无需重启。案例

    • 修改系统主题颜色,所有界面组件自动切换配色

    • 动态调整日志级别,实时生效

  6. 分布式系统中的一致性保证
    场景:多个服务需要根据核心服务状态变化保持一致性。案例

    • 电商系统中,订单状态变为“已支付”时,通知库存服务扣减库存、物流服务生成运单

    • 分布式缓存失效:当缓存数据更新,通知所有节点清除旧缓存

场景:以气象站为例,天气信息表示被观察者,天气布告板表示订阅者和观察者,当天气发生变化(被观察者)时,会通过notifyObserver通知所有观察者,并调用他们的控制方法处理数据。

一个WeatherData对象负责追踪目前的天气状况(温度,湿度,气压)。希望能建立一个应用,有三种布告板,分别显示目前的状况、气象统计及简单的预报。当WeatherObject对象获得最新的测量数据时,三种布告板必须实时更新。

气象监测应用的对象分析

此系统中的三个部分是:

  • 气象站(获取实际气象数据的物理装置)
  • WeatherData对象(最总来自气象站的数据,并更新布告板)
  • 布告板(显示目前天气状况给用户看)。

代码实现

  1. 实现气象站

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // 主题接口
    interface Subject{
    public void registerObserver(Observer o); // 注册观察者
    public void removeObserver(Observer o); // 删除观察者
    public void notifyObserver(); // 当主题状态改变时,这个方法会被调用,以通知所有的观察者
    }
    // 当气象观测值改变时,主题会把这些状态值当作方法的参数,传送给观察者
    interface Observer {
    public void update(float temp, float humidity, float pressure);
    }
    // 当布告板需要显示时,调用此方法
    interface Display {
    public void display();
    }
  2. WeatherData中实现主题接口

    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
    class WeatherData implements Subject{
    private ArrayList<Observer> observers;
    private float temperature;
    private float humidity;
    private float pressure;

    public WeatherData(){
    observers = new ArrayList<Observer>();
    }

    @Override
    public void registerObserver(Observer o) {
    observers.add(o);
    }

    @Override
    public void removeObserver(Observer o) {
    int i = observers.indexOf(o);
    if(i >= 0){
    observers.remove(i);
    }
    }

    @Override
    public void notifyObserver() {
    for(Observer observer:observers){
    observer.update(temperature, humidity, pressure);
    }
    }
    // 当从气象站得到更新观测值时,通知观察者
    public void measurementsChanged(){
    notifyObserver();
    }

    public void setMeasurements(float temperature, float humidity, float pressure){
    this.temperature = temperature;
    this.humidity = humidity;
    this.pressure = pressure;
    measurementsChanged();
    }
    // WeatherData的其他方法
    }
  3. 建立布告板(其中的一个布告板)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    class CurrentConditionDisplay implements Observer, DisplayElement {
    private float temperature; // 温度
    private float humidity; // 湿度
    private float pressure; // 气压
    private Subject weatherData; // 天气主题

    public CurrentConditionDisplay(Subject weatherData){
    this.weatherData = weatherData;
    weatherData.registerObserver(this);
    }

    @Override
    public void display() {
    System.out.println("这里气象台更新的天气数据...");
    }

    @Override
    public void update(float temp, float humidity, float pressure) {
    this.temperature = temp;
    this.humidity = humidity;
    this.pressure = pressure
    display();
    }
    }
  4. 利用内置的支持重写WeatherData

    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
    class WeatherDataTWO extends Observable {
        private float temperature;
        private float humidity;
        private float pressure;

        public WeatherDataTWO(){}

        public void measurementsChanged(){
            setChanged();   // 在调用 notifyObservers()之前,要先调用 setChanged() 来指示状态已经改变
            notifyObservers(); // 没调用 notifyObservers 传送数据对象,表示采用的做法是拉
        }

        public void setMeasurements(float temperature,float humidity,float pressure){
            this.temperature = temperature;
            this.humidity = humidity;
            this.pressure = pressure;
            measurementsChanged();
        }

        public float getTemperature() {
            return temperature;
        }

        public float getHumidity() {
            return humidity;
        }

        public float getPressure() {
            return pressure;
        }
    }
  5. 利用内置观察者重写布告板

    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
    @Slf4j
    class CurrentConditionsDisplay implements java.util.Observer, DisplayElement {

    Observable observable;
    private float temperature;
    private float humidity;

    public CurrentConditionsDisplay(Observable observable){
    this.observable = observable;
    observable.addObserver(this);
    }

    @Override
    public void display() {
    log.info("这里气象台更新的天气数据...");
    }

    @Override
    public void update(Observable o, Object arg) {
    if(o instanceof WeatherDataTWO) {
    WeatherDataTWO weatherDataTWO = (WeatherDataTWO) o;
    this.temperature = weatherDataTWO.getTemperature();
    this.humidity = weatherDataTWO.getHumidity();
    display();
    }
    }
    }

模板方法模式

模板方法(Template Method) 是一种行为设计模式。模板方法设计模式用于创建方法存根并将某些实现步骤推迟到子类。

模板方法定义了执行算法的步骤,它可以提供可能对所有或者部分子类通用的默认实现,下面通过一个简单的例子来理解这个模式。

应用场景

  1. 多个类有相似的算法结构

    多个类具有相似的行为或算法,只是某些步骤的实现细节不同,可以使用模板方法模式将这些相似的算法提取到一个抽象类中,将不同的步骤定义为抽象方法,由子类去实现。例如,在图形绘制程序中,绘制不同类型的图形(如圆形、矩形等)可能有相似的步骤,如初始化绘制环境、绘制图形轮廓、填充图形等,但具体的绘制轮廓和填充方式不同。

  2. 控制子类的扩展

    模板方法模式允许在不改变算法结构的情况下,通过子类扩展来改变算法的某些步骤。例如,在一个订单处理系统中,订单的处理流程可能包括接收订单、验证订单、处理支付、发货等步骤,其中处理支付的步骤可能会因支付方式的不同而有所差异。便可将订单处理流程定义在抽象类中,将支付处理步骤定义为抽象方法,由不同的子类实现不同的支付方式,同时保证订单处理基本流程不变。

  3. 框架设计

    框架提供了一个算法的骨架,开发者可通过继承框架中的抽象类并实现抽象方法来定制自己的功能。例如,在Java的JDBC框架中,Statement接口及其子类就体现了模板方法模式的思想。Statement接口定义了一系列执行SQL语句的方法,如executeQuery()executeUpdate()等,这些方法定义了执行SQL语句的基本流程,而具体的数据库操作由不同的数据库驱动程序实现。

  4. 一次性实现不变部分,避免代码重复

    当算法中的某些步骤是不变的,而其他步骤可能会变化时,可将不变的步骤在抽象类的模板方法中实现,变化的步骤定义为抽象方法由子类实现,这样避免了在多个子类中重复编写不变的代码。例如,在一个日志记录系统中,日志的格式和输出方式可能是不变的,但日志的内容可能会因不同的业务场景而不同,可使用模板方法模式将日志的格式和输出方式在抽象类中实现,将日志内容的生成定义为抽象方法,由具体的日志记录类实现。

代码实现

场景:假设想提供一种算法了解房子,建造房屋需要执行的步骤是:建造地基->建造支柱->建造墙壁和窗户。重点是不能改变执行的顺序,在这种情况下,可以创建一个模板方法,它将使用不同的方法来建造房子,现在盖房子的地基对于所有类型的房子都是一样的,无论是木房、玻璃房子还是混泥土房。所以可以为此提供基础实现,如果子类想要覆盖这个方法,他们可以自己选择,但大多数情况下,所有类型的房屋都很常见。为了确保子类不覆盖模板方法,应该将其设为最终方法。

模板方法抽象类:若希望某些方法由子类实现,必须将基类设为抽象类。

  1. 定义抽象类HouseTemplate

    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
    public abstract class HouseTemplate {
    // buildHouse()是模板方法,定义个执行几个步骤的执行顺序
    // template method, final so subclasses can't override final修饰,子类不能重写
    public final void buildHouse() {
    buildFoundation(); // 建造地基
    buildPillars(); // 建造柱子
    buildWalls(); // 建造墙壁
    buildWindows(); // 建造窗户
    System.out.println("House is built successfully");
    }

    // Building foundation
    private void buildFoundation() {
    System.out.println("Building foundation with cement, iron rods and sand");
    }

    // methods to be implemented by subclasses
    public abstract void buildPillars();
    public abstract void buildWalls();

    // default implementation
    private void buildWindows() {
    System.out.println("Building Glass Windows");
    }
    }
  2. WoodenHouseGlassHouseConcreteHouse

    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
    // 木房 WoodenHouse
    @Slf4j
    public class WoodenHouse extends HouseTemplate {
    @Override
    public void buildPillars() {
    log.info("Building Pillars With Wood coating...");
    }

    @Override
    public void buildWalls() {
    log.info("Building Wooden Walls...");
    }
    }

    // 玻璃房 GlassHouse
    @Slf4j
    public class GlassHouse extends HouseTemplate {
    @Override
    public void buildPillars() {
    log.info("Building Pillars With Glass coating...");
    }

    @Override
    public void buildWalls() {
    log.info("Building Glass Walls...");
    }
    }

    // 混泥土房屋 ConcreteHouse
    @Slf4j
    public class ConcreteHouse extends HouseTemplate {
    @Override
    public void buildPillars() {
    log.info("Building Pillars With Concrete coating...");
    }

    @Override
    public void buildWalls() {
    log.info("Building Concrete Walls...");
    }
    }
  3. HousingClient

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class HousingClient {
    public static void main(String[] args) {
    HouseTemplate houseBuilder = new WoodenHouse();
    houseBuilder.buildHouse();
    System.out.println("--------------");
    houseBuilder = new GlassHouse();
    houseBuilder.buildHouse();
    System.out.println("--------------");
    houseBuilder = new ConcreteHouse();
    houseBuilder.buildHouse();
    }
    }
  4. 输出结果

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    Building foundation with cement,iron rods and sand
    Building Pillars With Wood coating...
    Building Wooden Walls...
    Building Glass Windows
    House is built successfully
    --------------
    Building foundation with cement,iron rods and sand
    Building Pillars With Glass coating...
    Building Glass Walls...
    Building Glass Windows
    House is built successfully
    --------------
    Building foundation with cement,iron rods and sand
    Building Pillars With Concrete coating...
    Building Concrete Walls...
    Building Glass Windows
    House is built successfully

    Process finished with exit code 0