走进 Gang of Four 设计模式:代理模式

走进 Gang of Four 设计模式:代理模式

说明:不仅告诉你"怎么用",更告诉你"为什么这样设计"

前置知识:Java 面向对象基础(接口、多态)、静态代理与动态代理概念


📊 设计模式分类总览

GoF 23 种设计模式可按两个维度交叉分类:类/对象 (处理方式) × 创建型/结构型/行为型(目的)。

维度 创建型(Creational) 结构型(Structural) 行为型(Behavioral)
类(Class) (通过继承复用) Factory Method 工厂方法 Adapter(类适配器) Interpreter(解释器) Template Method(模板方法)
对象(Object) (通过组合/聚合复用) Abstract Factory(抽象工厂) Builder(建造者) Prototype(原型) Singleton(单例) Adapter(对象适配器) Bridge(桥接) Composite(组合) Decorator(装饰) Facade(外观) Flyweight(享元) Proxy(代理) Chain of Resp.(责任链) Command(命令) Iterator(迭代器) Mediator(中介者) Memento(备忘录) Observer(观察者) State(状态) Strategy(策略) Visitor(访问者)

本文档聚焦 结构型-对象模式 中的 Proxy(代理) ,核心思想是通过引入一个中间层(Proxy)控制对真实对象的访问,在不修改原始类的前提下添加额外逻辑。


📑 目录

  1. [模式概述 · 15 问深度分析](#模式概述 · 15 问深度分析)
    • [Q1: 为什么需要?解决了什么问题?](#Q1: 为什么需要?解决了什么问题?)
    • [Q2: 不用会有什么缺陷?](#Q2: 不用会有什么缺陷?)
    • [Q3: 核心思想一句话](#Q3: 核心思想一句话)
    • [Q4: 包含哪些角色?](#Q4: 包含哪些角色?)
    • [Q5: 调用流程是怎样的?](#Q5: 调用流程是怎样的?)
    • [Q6: 为什么这样设计?](#Q6: 为什么这样设计?)
    • [Q7: 为什么用接口/抽象类/组合?](#Q7: 为什么用接口/抽象类/组合?)
    • [Q8: 符合哪些面向对象原则?](#Q8: 符合哪些面向对象原则?)
    • [Q9: 优缺点与额外成本](#Q9: 优缺点与额外成本)
    • [Q10: 适合/不该用的场景](#Q10: 适合/不该用的场景)
    • [Q11: 与其他模式的区别](#Q11: 与其他模式的区别)
    • [Q12: 框架中的典型应用](#Q12: 框架中的典型应用)
    • [Q13: 最小可运行版本](#Q13: 最小可运行版本)
    • [Q14: 识别重构信号](#Q14: 识别重构信号)
    • [Q15: 现代改进方案](#Q15: 现代改进方案)
  2. 框架源码实战分析
    • [Spring AOP 代理机制](#Spring AOP 代理机制)
    • [MyBatis Mapper 动态代理](#MyBatis Mapper 动态代理)
    • [Tomcat RequestFacade 安全代理](#Tomcat RequestFacade 安全代理)
  3. [深度追问:Why / How / Trade-off / Evolution / Modern Practice](#深度追问:Why / How / Trade-off / Evolution / Modern Practice)
  4. 总结
  5. 参考文献

1. 模式概述 · 15 问深度分析

Q1: 为什么需要这个模式?它解决了什么问题?

在软件开发中,直接访问一个对象有时会带来问题。例如:对象创建开销过大、需要进行权限控制、或者对象存在于远程机器上。

代理模式解决的核心问题是:在不改变原始类代码的前提下,控制对该对象的访问,并在访问前后添加额外的间接层(控制逻辑)。

Q2: 如果不用这个模式,会有什么缺陷?

不用代理模式,你只能将非业务核心逻辑(如日志记录、权限校验、事务管理、缓存等)硬编码进业务类中。这会导致:

  • 代码臃肿与耦合: 核心业务代码充斥着大量与业务无关的周边逻辑。
  • 违反单一职责原则: 一个类既要负责核心业务,又要负责安全、日志和事务。
  • 难以扩展和复用: 每次你想给不同类加上相同的安全校验,都得复制一遍代码。
java 复制代码
// ❌ 业务代码中混入了横切逻辑
public class UserService {
    public void createUser(User user) {
        // 权限校验
        if (!SecurityContext.hasRole("ADMIN")) {
            throw new SecurityException("无权限");
        }
        // 日志记录
        log.info("开始创建用户...");
        // 事务开启
        TransactionManager.begin();
        try {
            // 核心业务逻辑
            userDao.insert(user);
            TransactionManager.commit();
        } catch (Exception e) {
            TransactionManager.rollback();
            throw e;
        }
        log.info("用户创建成功");
    }
}

Q3: 核心思想是什么?一句话如何概括?

  • 核心思想: 通过引入一个代理对象来间接访问目标对象,从而在不修改目标对象的前提下,控制访问并为其扩展功能。
  • 一句话概括: "房东只管签合同,出租找中介" ------ 用一个替身来控制和增强对原对象的访问。

Q4: 它包含哪些角色?每个角色的职责是什么?

代理模式通常包含以下 3 个核心角色:

  1. 抽象主题(Subject): 定义了真实主题和代理主题的共同接口。这样在任何使用真实主题的地方,都可以用代理主题代替。
  2. 真实主题(Real Subject): 真正执行核心业务逻辑的对象,是代理对象所代表的幕后真实对象。
  3. 代理(Proxy): 内部持有对真实主题的引用。它与真实主题实现了相同的接口,因此可以替代真实主题。它负责在调用真实主题前后,执行预处理和善后工作(如权限判定、延迟加载、日志记录)。

Q5: 它们之间如何协作?调用流程是怎样的?

调用流程如下:

  1. 客户端(Client) 拿着抽象主题(Subject)的引用,调用其方法。此时客户端实际调用的是代理(Proxy)
  2. 代理(Proxy) 接收到请求,先执行前置处理(例如:检查用户是否有权限,或者判断真实对象是否需要初始化)。
  3. 代理(Proxy) 调用**真实主题(Real Subject)**的对应方法,执行核心业务。
  4. 代理(Proxy) 接收到真实主题的返回值,执行后置处理(例如:记录日志、提交事务、缓存结果)。
  5. 代理(Proxy) 将最终结果返回给客户端。

Q6: 为什么要这样设计?每个角色存在的意义是什么?

  • 抽象主题(Subject)存在的意义: 保证了"透明性"。对客户端而言,它不知道也不关心自己拿到的是真身还是代理,两者的契约(接口)完全一致,这极大地降低了系统的耦合度。
  • 真实主题(Real Subject)存在的意义: 专注核心业务逻辑。它不需要感知任何诸如网络传输、权限控制或日志统计的外部环境,保持纯粹。
  • 代理(Proxy)存在的意义: 充当"挡箭牌"和"打杂工"。它屏蔽了直接访问真身带来的副作用,承接了所有横切关注点(Cross-cutting Concerns)。

Q7: 为什么使用接口、抽象类、组合,而不是其他方式?

  • 为什么用接口/抽象类(继承契约): 为了实现多态。只有当代理和真实对象拥有相同的"外壳"(接口)时,客户端才能无感知地进行切换。
  • 为什么用组合(内部持有真实对象),而不是让代理类直接继承真实类:
    • 继承是静态的(编译期决定),而组合是动态的(运行期决定)。
    • 如果使用继承,每个真实类都需要写一个继承它的代理类,会导致类爆炸
    • 通过组合,一个代理类甚至可以代理一类实现了相同接口的不同真实对象(尤其是动态代理)。

Q8: 这种设计符合哪些面向对象原则?

  • 开闭原则(OCP): 当你需要增加新的控制逻辑(如新增缓存功能)时,不需要修改真实主题的代码,只需要新建一个代理类或者扩展代理逻辑即可。
  • 单一职责原则(SRP): 真实主题只负责核心业务,代理类负责控制和周边外围逻辑,各自职责单一。
  • 依赖倒置原则(DIP): 客户端和代理类都依赖于抽象接口(Subject),而不是依赖于具体的实现类(Real Subject)。

Q9: 它有哪些优点和局限性?会带来哪些额外成本?

优点:

  • 能协调调用者和被调用者,在一定程度上降低了系统的耦合度。
  • 可以在不修改原始代码的情况下,动态地切入新功能。
  • 远程代理可以隐藏对象存在于不同地址空间的事实;虚拟代理可以优化性能(延迟加载)。

局限性/额外成本:

  • 性能损耗: 增加了一层间接调用,请求处理速度可能会稍微变慢。
  • 复杂度增加: 增加了类的数量(静态代理),或者让代码的调用链路变长,增加了调试和理解的成本(尤其是多层动态代理)。

Q10: 它最适合哪些场景?哪些场景不应该使用?

适合使用的场景:

  • 虚拟代理(Virtual Proxy): 用于大对象的延迟加载(Lazy Loading),比如网页中先用一张轻量级占位图代替未加载完的大图。
  • 保护代理(Protection Proxy): 控制不同用户对真实对象的访问权限。
  • 远程代理(Remote Proxy): 为一个位于不同地址空间(如微服务、RPC 调用)的对象提供局部代表(如 Dubbo 的 Stub)。
  • 智能代理(Smart Proxy): 在方法前后统一加日志、统计耗时或开启/提交事务。

不应该使用的场景:

  • 如果系统调用链路已经非常冗长且性能敏感,不应滥用代理。
  • 如果扩展功能只需要简单地在真身内部改动一行代码,且该类未来几乎不会再扩展其他外围功能,引入代理属于过度设计。

Q11: 它与容易混淆的其他设计模式有什么区别和联系?

  • 装饰器模式(Decorator) vs 代理模式:
    • 目的不同: 装饰器关注于增强功能 (动态地为对象添加职责);代理关注于控制访问,它往往对客户端隐瞒真实对象的细节。
  • 适配器模式(Adapter) vs 代理模式:
    • 接口不同: 适配器会改变接口 (把 A 接口转成 B 接口);代理模式严格保持接口一致
  • 外观模式(Facade) vs 代理模式:
    • 粒度不同: 外观模式为一组子系统 定义一个高层组合接口(一对多);代理模式代表的是单个对象(一对一)。

Q12: Java/JDK、Spring、MyBatis、Tomcat 等框架中有哪些典型应用?

  • JDK: java.lang.reflect.Proxy 提供了动态代理的底层实现;Collections.unmodifiableList() 返回一个只读的代理列表,当你调用 add() 时会抛出异常(保护代理)。
  • Spring: Spring AOP(面向切面编程)的基石就是代理模式。 它通过 JDK 动态代理或 CGLIB 动态代理,实现了 @Transactional 事务控制和日志切面。
  • MyBatis: Mapper 接口(如 UserMapper)并没有实现类,MyBatis 内部使用 JDK 动态代理为这些接口生成了代理对象,拦截方法调用并将其转化为执行 SQL。
  • Tomcat: Request 和 Response 对象在传入 Servlet 时,往往会被包装成 RequestFacadeResponseFacade(外观/代理结合体),防止开发者通过强转接触到 Tomcat 容器底层的核心 Request 对象,确保安全。

Q13: 如何从零实现一个最小可运行版本?

以图片懒加载为例:

java 复制代码
// Subject
public interface Image {
    void display();
}

// RealSubject
public class RealImage implements Image {
    private String fileName;

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

    private void loadFromDisk(String fileName) {
        System.out.println("Loading " + fileName);
    }

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

// Proxy
public class ProxyImage implements Image {
    private RealImage realImage;
    private String fileName;

    public ProxyImage(String fileName) {
        this.fileName = fileName;
        // 注意:构造时不加载图片
    }

    @Override
    public void display() {
        // 延迟加载:只在第一次调用 display() 时创建 RealImage
        if (realImage == null) {
            realImage = new RealImage(fileName);
        }
        realImage.display();
    }
}

// Client
public class ProxyPatternDemo {
    public static void main(String[] args) {
        Image image = new ProxyImage("test_10mb.jpg");
        System.out.println("Image created, but not yet loaded.");

        // 第一次调用 display(),才真正加载
        image.display();
        System.out.println("");

        // 第二次调用,不需要再加载,直接显示
        image.display();
    }
}

Q14: 如何在现有项目中识别可以使用该模式的地方?

如果代码中频繁出现"在执行核心逻辑之前,先做某件事"的模式(如先检查权限再执行、先加载缓存再执行、先记录日志再执行),说明需要引入代理模式将这些横切关注点剥离。

典型信号:

  1. 业务类中充斥着 log.info("start") → 核心逻辑 → log.info("end") 的三段式代码。
  2. 业务类中每次方法调用前都有 if (!hasPermission(...)) throw ...
  3. 业务类的方法中包含 TransactionManager.begin()TransactionManager.commit()

Q15: 如果重新设计这个模式,在今天会有哪些改进?

静态代理 → 动态代理: 静态代理中,代理类在编译期就已经写好,每增加一个真实对象就需要手动编写一个代理类,代码量翻倍。动态代理(JDK 动态代理或 CGLIB)允许在运行时动态生成代理对象,消除了代理类的数量问题。

Spring AOP 让代理变得透明: 开发不再需要手动编写代理类,只需声明 @Transactional@Cacheable 等注解,Spring 自动为 Bean 生成代理。

java 复制代码
// 使用注解即可,无需手动编写代理
@Service
public class UserService {
    @Transactional
    public void createUser(User user) {
        // 只写核心业务逻辑,事务由代理自动处理
        userDao.insert(user);
    }
}

Sidecar 模式(云原生): 在 Kubernetes + Istio 架构中,每个微服务旁边部署一个代理进程(Envoy),负责处理所有进出流量、服务发现、熔断限流。这是代理模式从代码层面到基础设施层面的升华。


2. 框架源码实战分析

2.1 Spring AOP 代理机制

Spring AOP 是代理模式最经典的工业级应用。它的核心是:在运行时为目标对象生成代理,在代理中织入切面逻辑(事务、安全、日志等)。

JDK 动态代理 vs CGLIB 代理:

代理方式 适用条件 原理
JDK 动态代理 目标类实现了接口 通过 Proxy.newProxyInstance() 生成实现了相同接口的代理类
CGLIB 代理 目标类未实现接口 通过生成目标类的子类作为代理(EmitSubclass)

Spring 源码核心逻辑(简化):

java 复制代码
// Spring AOP 的核心------代理创建
public class DefaultAopProxyFactory implements AopProxyFactory {
    @Override
    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        // 判断使用 JDK 动态代理还是 CGLIB
        if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
            // 没有接口 → 使用 CGLIB
            return new ObjenesisCglibAopProxy(config);
        } else {
            // 有接口 → 使用 JDK 动态代理
            return new JdkDynamicAopProxy(config);
        }
    }
}

// JDK 动态代理的调用处理程序
public class JdkDynamicAopProxy implements AopProxy, InvocationHandler {
    // 持有目标对象的引用
    private final AdvisedSupport advised;

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, ...);
        
        // 1. 执行前置通知(@Before)
        // 2. 执行目标方法
        Object retVal = invocation.proceed();
        // 3. 执行后置通知(@AfterReturning / @After)
        // 4. 返回结果
        
        return retVal;
    }
}

运行时调用链:

当你调用 userService.createUser(user) 时:

  1. 实际上调用的是 Spring 生成的代理对象的 createUser() 方法。
  2. 代理对象进入 JdkDynamicAopProxy.invoke()
  3. invoke 方法获取该方法对应的拦截器链(包括事务拦截器、日志拦截器等)。
  4. 逐个执行拦截器:先开启事务,然后调用目标方法,最后提交事务或回滚。
  5. 整个过程中,UserService 完全不需要知道事务的存在。

2.2 MyBatis Mapper 动态代理

MyBatis 中,我们只需要定义 Mapper 接口而无需编写实现类:

java 复制代码
// 定义接口 ------ 不需要写实现类
public interface UserMapper {
    @Select("SELECT * FROM user WHERE id = #{id}")
    User selectById(Long id);
}

MyBatis 使用 JDK 动态代理为每个 Mapper 接口生成代理对象:

java 复制代码
// MyBatis 核心------Mapper 代理
public class MapperProxyFactory<T> {
    private final Class<T> mapperInterface;

    @SuppressWarnings("unchecked")
    protected T newInstance(MapperProxy<T> mapperProxy) {
        // 使用 JDK 动态代理生成 Mapper 接口的代理对象
        return (T) Proxy.newProxyInstance(
            mapperInterface.getClassLoader(),
            new Class[] { mapperInterface },
            mapperProxy  // 调用处理程序
        );
    }
}

// MapperProxy------InvocationHandler
public class MapperProxy<T> implements InvocationHandler, Serializable {
    private final SqlSession sqlSession;
    private final Class<T> mapperInterface;

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 1. 如果是 Object 的方法(toString, hashCode 等),直接调用
        if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
        }

        // 2. 根据 Method 从配置中查找对应的 SQL 语句(MappedStatement)
        MappedStatement ms = configuration.getMappedStatement(method.getName());

        // 3. 执行 SQL(代理的核心职责)
        return sqlSession.selectOne(ms, args);
    }
}

运行时调用链: 当你调用 userMapper.selectById(1) 时,实际调用的是 MapperProxy.invoke() → 获取 SQL 语句 → 创建 SqlSession → 执行 JDBC 操作 → 映射结果集 → 返回 User 对象。对业务代码来说,感觉就像在调用一个本地方法。

2.3 Tomcat RequestFacade 安全代理

Tomcat 在处理 HTTP 请求时,内部使用了 org.apache.catalina.connector.Request 对象来处理底层 socket 数据。但这个对象暴露了太多内部 API(如 getCoyoteRequest()getConnector() 等),如果直接传给 Servlet 容器,开发者可以强转后访问 Tomcat 的内部实现,存在安全隐患。

解决方案:使用 RequestFacade 作为代理:

java 复制代码
// Tomcat 内部的核心 Request 对象(包含大量的内部方法)
public class Request implements HttpServletRequest {
    // 内部方法------不希望暴露给 Servlet 开发者
    public Connector getConnector() { ... }
    public CoyoteRequest getCoyoteRequest() { ... }
    
    // 标准 Servlet 方法
    @Override
    public String getParameter(String name) { ... }
    @Override
    public String getRequestURI() { ... }
}

// RequestFacade------代理(只暴露标准 Servlet 接口)
public class RequestFacade implements HttpServletRequest {
    // 持有内部 Request 对象的引用(组合)
    private final Request request;

    public RequestFacade(Request request) {
        this.request = request;
    }

    // 只代理标准方法
    @Override
    public String getParameter(String name) {
        return request.getParameter(name);
    }

    @Override
    public String getRequestURI() {
        return request.getRequestURI();
    }

    // 注意:没有代理 getConnector()、getCoyoteRequest() 等内部方法
    // Servlet 开发者无法通过 RequestFacade 访问 Tomcat 内部实现
}

为什么这样设计? 这是保护代理 的典型应用。Tomcat 通过 RequestFacade 控制了对底层 Request 对象的访问权限------只允许开发者调用标准 Servlet API,屏蔽了内部实现细节。这既保证了接口的兼容性,又保护了容器内部的安全。


3. 深度追问

3.1 Why(为什么):解决哪个根本矛盾?

代理模式解决的是**"核心业务职责的纯粹性"与"系统横切关注点(Cross-cutting Concerns)的侵入性"之间的根本矛盾。**

安全、事务、日志、缓存、延迟加载等横切逻辑与核心业务逻辑本质上是正交的,但传统的 OOP 缺乏有效的分离机制,导致这些关注点散落在各个业务类中,造成代码的混乱和重复。代理模式通过创建一个中间层,将这些横切关注点从核心业务中剥离出来,实现了关注点分离

3.2 How(怎么做):对象关系与交互机制

代理模式的实现有三个层级,逐步递进:

静态代理: 手动编写代理类,编译期就已确定。优点是简单直接;缺点是一个代理类只能服务一个接口,真实对象增加时代码量翻倍。

JDK 动态代理: 运行时通过 Proxy.newProxyInstance() 为接口生成代理对象,使用 InvocationHandler 拦截方法调用。优点是无需手动编写代理类;缺点是只能代理实现了接口的类。

CGLIB 代理: 运行时通过字节码生成技术(ASM)创建目标类的子类作为代理。优点是可以代理没有接口的普通类;缺点是 final 方法无法被代理,且生成速度比 JDK 代理慢。

三种方式的共同核心:代理与真实对象实现相同的接口(或继承相同的基类),客户端面向接口编程,对代理的存在无感知。

3.3 Trade-off(代价):牺牲了什么换取什么?

它牺牲了(付出代价):

  • 性能开销: 每次方法调用都多了一层间接调用,在高频调用场景下性能损耗不可忽略(尤其是动态代理的反射调用)。
  • 调试复杂度: 代理在 IDE 中断点调试时,调用栈比直接调用深 2-3 层。Spring AOP 的代理 + AOP 拦截器链可能让调用栈深达 5-8 层。
  • 启动时间: 动态代理在启动时需要生成字节码、加载类,会延长应用的启动时间。

它换取了(获得收益):

  • 完美的关注点分离: 核心业务与横切逻辑完全解耦。
  • 极高的可扩展性: 新增横切逻辑只需增加新的代理或切面,不改现有业务逻辑。
  • 对客户端的完全透明: 客户端感知不到代理的存在,面向接口编程即可。

3.4 Evolution(演化):它是如何从简单设计逐步演化而来的?

  1. 硬编码横切逻辑(起点): 在业务代码中直接写日志、事务、权限检查。最直接但也最混乱。

  2. 静态代理(抽取): 为每个业务类编写对应的代理类,将横切逻辑抽取到代理中。但代码量翻倍,维护成本高。

  3. JDK 动态代理(动态化): 运行时根据接口动态生成代理对象,消除了代理类的数量问题。但只能代理接口。

  4. CGLIB 代理(突破限制): 通过生成子类的方式代理普通类,突破了 JDK 代理只能代理接口的限制。

  5. AOP 框架(声明式): Spring AOP 结合 IoC 容器,让开发者只需要声明 @Transactional@Cacheable 等注解,Spring 自动在运行时为目标 Bean 生成合适的代理。开发者几乎感知不到代理的存在。

3.5 Modern Practice(现代实践):它在今天被取代了吗?

结论:代理模式不仅没有被取代,反而在现代框架中成为了基础设施级的核心机制。

  • Spring AOP 是代理模式最成功的工业应用,@Transactional@Cacheable@Async 等注解全部依赖代理实现。
  • MyBatis Mapper 动态代理 让开发者不需要编写 DAO 实现类。
  • Dubbo / gRPC 的远程代理 让远程调用像本地调用一样简单。

Sidecar 模式------代理模式在基础设施层面的升华: 在云原生架构(Kubernetes + Istio)中,每个微服务旁边部署一个代理进程(Envoy),负责处理所有进出流量、服务发现、熔断限流、可观测性。业务代码完全不需要关心这些基础设施能力------这正是代理模式"透明控制"思想的终极体现。


4. 总结

核心要点

  • 代理模式为真实对象提供一个替身,通过中间层控制访问并添加额外逻辑
  • 三种实现层级:静态代理 → JDK 动态代理 → CGLIB 代理
  • 与装饰器的区别:代理控制访问 ,装饰器增强功能
  • Spring AOP 是代理模式的核心应用------通过 @Transactional 等注解声明式管理横切逻辑
  • 在云原生架构中,代理模式升级为 Sidecar 模式,成为基础设施的一等公民

一句话

代理模式就是"中间人"------你找明星演出,通过经纪人(代理)谈价格、排档期,明星只负责登台表演。


5. 参考文献

1 GAMMA E, HELM R, JOHNSON R, et al. Design Patterns: Elements of Reusable Object-Oriented SoftwareM. Boston: Addison-Wesley, 1994.

2 SPRING. Spring AOP DocumentationEB/OL. https://docs.spring.io/spring-framework/reference/core/aop.html, 2024.

3 MyBatis. MyBatis 3 Source CodeEB/OL. https://github.com/mybatis/mybatis-3, 2024.

4 Apache Tomcat. Tomcat Source CodeEB/OL. https://tomcat.apache.org/, 2024.

相关推荐
我登哥MVP2 小时前
走进 Gang of Four 设计模式:外观模式
java·设计模式·外观模式
ttod_qzstudio15 小时前
【软考设计模式】备忘录模式:对象状态的捕获与无损恢复精讲
设计模式·备忘录模式
ttod_qzstudio15 小时前
【软考设计模式】责任链模式:请求传递的多级处理与发送接收解耦精讲
设计模式·责任链模式
2501_9142459316 小时前
C语言设计模式详解:从理论到实践的完整指南
c语言·开发语言·设计模式
CaffeinePro20 小时前
四⼤极简架构原则KISS/DRY/YAGNI/LOD
设计模式·架构
霸道流氓气质21 小时前
SpringBoot中设计模式组合使用示例-策略、模板、观察者、门面、工厂、单例。
spring boot·后端·设计模式
ttod_qzstudio1 天前
【软考设计模式】观察者模式:一对多依赖的自动通知与订阅解耦精讲
观察者模式·设计模式
ttod_qzstudio1 天前
【软考设计模式】命令模式:请求封装与调用接收解耦的精讲
设计模式·命令模式
春卷同学1 天前
HarmonyOS掌上记账APP开发实践第5篇:鸿蒙 EventBus 设计模式 — MoneyTrack 跨模块通信解密
华为·设计模式·harmonyos