设计模式-装饰器模式(结构型)与责任链模式(行为型)对比,以及链式设计

news2025/2/28 15:46:21

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 1.装饰器模式
    • 1.1概念
    • 1.2作用
    • 1.3应用场景
    • 1.4特点
    • 1.5类与对象关系
    • 1.6实现
  • 2责任链模式
    • 2.1概念
    • 2.2作用
    • 2.3应用场景
    • 2.4特点
    • 2.5类与对象关系
    • 2.6实现
  • 3.对比
  • 总结


前言

装饰器模式和设计模式同为GoF23中设计模式之一,两者在结构上有相似之处,本文章用来学习两个设计模式,并进行对比。


1.装饰器模式

1.1概念

如果我们想要为一个已经写好的类增加新功能,在不修改该类源码的情况下,我们有以下选择:

  • 创建该类的子类,并用该子类调用父类方法,并额外添加其他功能
  • 创建一个新的类,引用已有类的对象,调用原有方法并添加其他功能

这里第二种实现思路就是我们现在说的装饰器模式。

1.2作用

为一个已有的类增加新的功能。

1.3应用场景

  • 需要动态地给对象添加职责时:当你需要给某个对象添加职责,但又不想修改其类定义时,可以使用装饰器模式。
  • 需要灵活组合多个职责时:当你有多个职责需要组合,并且这些职责的排列组合可能会变化时,装饰器模式是一个很好的选择。
  • 需要保持类的单一职责原则时:当你想保持类的单一职责原则,但又需要给类添加额外的职责时,可以使用装饰器模式将这些职责分离到装饰器

1.4特点

  • 动态扩展:装饰器模式能够在运行时动态地给对象添加职责,而无需修改类的定义。
  • 灵活性:通过组合的方式而不是继承来扩展功能,这使得装饰器模式比继承更加灵活。
  • 遵循开闭原则:装饰器模式可以在不修改现有代码的情况下添加新的功能,符合开闭原则(对扩展开放,对修改关闭)。
  • 装饰器与被装饰对象拥有相同的接口:这使得装饰器可以透明地替代被装饰对象

1.5类与对象关系

在这里插入图片描述

  • 组件接口:对应上图Component,用来规定被装饰对象和装饰器应该有哪些功能(或者说原组件中哪些功能是可以装饰的)
  • 组件类:对应上图ConcreteComponent,是被装饰对象
  • 抽象装饰器:对应上图Decorator,是一个接口,因为我们会有多个不同的具体装饰器,所以需要使用一个抽象装饰器接口来指定具体装饰器应该有哪些功能
  • 具体装饰器:是我们真正的额外功能编写的地方,因为实现了抽象装饰器所以拥有与被装饰对象相同但更强大的功能,可以在使用时进行替换。

1.6实现

// 定义一个接口,规定哪些方法可以被装饰
interface Component {
    void operation();
}
 
// 具体组件类,实现了Component接口
class ConcreteComponent implements Component {
    @Override
    public void operation() {
        System.out.println("ConcreteComponent operation");
    }
}
 
// 抽象装饰器类,持有一个Component对象,并实现了Component接口
abstract class Decorator implements Component {
  // 该对象存储原对象或者被其他装饰器装饰过的对象
  // 因为可以是其他装饰器装饰过的对象,所以才实现装饰器的叠加,是动态添加功能的关键
    protected Component component;
 
    public Decorator(Component component) {
        this.component = component;
    }
 
    @Override
    public void operation() {
        component.operation();
    }
}
 
// 具体装饰器类A,增加了额外的行为
class ConcreteDecoratorA extends Decorator {
    public ConcreteDecoratorA(Component component) {
        super(component);
    }
 
    @Override
    public void operation() {
        super.operation();
        addedBehavior();
    }
 
    private void addedBehavior() {
        System.out.println("ConcreteDecoratorA added behavior");
    }
}
 
// 具体装饰器类B,增加了额外的行为
class ConcreteDecoratorB extends Decorator {
    public ConcreteDecoratorB(Component component) {
        super(component);
    }
 
    @Override
    public void operation() {
        addedBehavior();
        super.operation();
    }
 
    private void addedBehavior() {
        System.out.println("ConcreteDecoratorB added behavior");
    }
}
 
// 客户端代码
public class DecoratorPatternDemo {
    public static void main(String[] args) {
        Component component = new ConcreteComponent();
 
        // 使用装饰器A装饰
        Component decoratorA = new ConcreteDecoratorA(component);
        // 使用装饰器B装饰装饰器A
        Component decoratorB = new ConcreteDecoratorB(decoratorA);
 
        // 执行操作,将展示所有组件和装饰器的行为
        decoratorB.operation();
    }
}

2责任链模式

2.1概念

责任链模式是一种行为设计模式,它允许你将请求沿着处理者链进行传递。每个处理者可以对请求进行处理,或者将请求传递给链中的下一个处理者。这使得你可以在不明确具体接收者的情况下,向多个对象发送请求。

2.2作用

  • 解耦请求与处理:请求者只要向链头发送一次请求,而不用关心具体由谁处理。
  • 可以动态管理处理类:可以按需添加或删除处理类。

2.3应用场景

  • 多个对象有机会处理请求:当请求可以在多个对象之间传递,并且每个对象都有可能处理它时。
  • 请求处理顺序可变:当请求的处理顺序不固定,或者你想在不修改代码的情况下改变处理顺序时。
  • 解耦请求发送者和处理者:当你想解耦请求发送者和处理者之间的依赖关系时。

2.4特点

  • 链式传递:请求在多个处理者之间传递,直到有一个处理者处理它或链结束。
  • 解耦请求发送者和接收者:发送者不需要知道哪个具体的处理者会处理请求,它只需要将请求发送到链的头部即可。
  • 增强灵活性:你可以通过动态地添加或删除处理者来改变链的结构,从而改变请求的处理流程。

2.5类与对象关系

在这里插入图片描述

责任链模式中我们有以下角色:

  • 抽象处理者(Handler)角色:声明“处理”方法,以及一个“设置下一处理器”的方法
  • 具体处理者(Concrete Handler)角色:实现了抽象处理者接口,并包含处理请求的逻辑。如果它不能处理请求,它会将请求传递给链中的下一个处理者。

2.6实现

// 抽象处理者接口
abstract class Handler {
    protected Handler nextHandler;
 
    // 设置下一个处理者
    public void setNextHandler(Handler nextHandler) {
        this.nextHandler = nextHandler;
    }
 
    // 处理请求的方法,由具体处理者实现
    public abstract void handleRequest(String request);
}


// 具体处理者类A
class ConcreteHandlerA extends Handler {
    @Override
    public void handleRequest(String request) {
        if ("A".equals(request)) {
            System.out.println("ConcreteHandlerA handled request: " + request);
        } else {
            if (nextHandler != null) {
                nextHandler.handleRequest(request);
            }
        }
    }
}


// 具体处理者类B
class ConcreteHandlerB extends Handler {
    @Override
    public void handleRequest(String request) {
        if ("B".equals(request)) {
            System.out.println("ConcreteHandlerB handled request: " + request);
        } else {
            if (nextHandler != null) {
                nextHandler.handleRequest(request);
            }
        }
    }
}


// 客户端代码
public class ChainOfResponsibilityDemo {
    public static void main(String[] args) {
        // 创建处理者对象
        Handler handlerA = new ConcreteHandlerA();
        Handler handlerB = new ConcreteHandlerB();
 
        // 构建处理者链
        handlerA.setNextHandler(handlerB);
 
        // 创建并发送请求
        String[] requests = {"A", "B", "C"};
 
        for (String request : requests) {
            handlerA.handleRequest(request); // 请求从handlerA开始传递
        }
    }
}

3.对比

  1. 首先,链式设计最重要的一点是——一个节点中要存储相邻节点的信息,这一点在上述两种设计模式中都有体现。这里要先清楚各自具体存储的是什么。
  • 装饰器模式存储的是原组件或被修饰过的组件,这说明在该种模式下各个装饰器的作用是可以叠加的
  • 责任链模式存储的是下一个处理节点,这里各个处理器效果并不会叠加
  1. 其次,各个节点应该有同样的任务。这两种模式中不论是各个装饰器,还是各个处理器,其实都有同样的任务。但是两个模式在具体实现时有区别,这也是装饰器模式可叠加而责任链模式不可叠加的原因。
  • 装饰器中的方法逻辑是——上一节点处理逻辑"及"本节点处理逻辑
  • 责任链中的方法逻辑是——本节点处理逻辑"或"下节点处理逻辑

总结

本文章比较了装饰器模式和责任链模式,因为两者在结构上十分相似都使用了链式设计,所以容易混淆,这里需要重点理解两者在处理方法中的实现逻辑的不同,装饰器是"且",责任链是"或"。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2259232.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【JavaEE】网络(2)

一、网络编程套接字 1.1 基础概念 【网络编程】指网络上的主机,通过不同的进程,以编程的方式实现网络通信;当然,我们只要满足进程不同就行,所以即便是同一个主机,只要是不同进程,基于网络来传…

题海拾贝:力扣 141.环形链表

Hello大家好&#xff01;很高兴我们又见面啦&#xff01;给生活添点passion&#xff0c;开始今天的编程之路&#xff01; 我的博客&#xff1a;<但凡. 我的专栏&#xff1a;《编程之路》、《数据结构与算法之美》、《题海拾贝》 欢迎点赞&#xff0c;关注&#xff01; 1、题…

SEC_ASA 第二天作业

拓扑 按照拓扑图配置 NTP&#xff0c;Server端为 Outside路由器&#xff0c;Client端为 ASA&#xff0c;两个设备的 NTP传输使用MD5做校验。&#xff08;安全 V4 LAB考点&#xff09; 提示&#xff1a;Outside路由器作为 Server端要配置好正确的时间和时区&#xff0c;ASA防…

IDEA 未启用lombok插件的Bug

项目中maven已引用了lombok依赖&#xff0c;之前运行没有问题的&#xff0c;但有时启动会提示&#xff1a; java: You arent using a compiler supported by lombok, so lombok will not work and has been disabled. Your processor is: com.sun.proxy.$Proxy8 Lombok support…

markdown入门

这里写自定义目录标题 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一个…

知识库系统,集成neo4j,集成activiti工作流,集成es全文检索,知识图谱血缘关系,nlp知识库

一、项目介绍 一款全源码&#xff0c;可二开&#xff0c;可基于云部署、私有部署的企业级知识库云平台&#xff0c;一款让企业知识变为实打实的数字财富的系统&#xff0c;应用在需要进行文档整理、分类、归集、检索、分析的场景。 为什么建立知识库平台&#xff1f; 助力企业…

AI技术架构:从基础设施到应用

人工智能&#xff08;AI&#xff09;的发展&#xff0c;正以前所未有的速度重塑我们的世界。了解AI技术架构&#xff0c;不仅能帮助我们看懂 AI 的底层逻辑&#xff0c;还能掌握其对各行业变革的潜力与方向。 一、基础设施层&#xff1a;AI 技术的坚实地基 基础设施层是 AI 技…

钉钉机器人消息推送类型案例

CSDN 目录展示 目录 钉钉机器人消息推送1- 文本text类型推送代码案例推送结果参数说明 2- 链接Link类型推送代码案例推送结果参数说明 3- Markdown类型推送代码案例1推送结果1推送代码案例2推送结果2推送代码案例2 (版本2)推送结果2(版本2)参数说明 4- 整体跳转ActionCard类型…

6_Sass 选择器函数 --[CSS预处理]

Sass 提供了一系列的选择器函数&#xff0c;用于操作和组合CSS选择器。这些函数可以帮助你更灵活地创建样式规则&#xff0c;并且可以减少重复代码。以下是几个常用的选择器函数及其用法&#xff1a; 1. selector-append($selector1, $selector2...) selector-append($select…

Wireshark如何查看数据包时间间隔

1.如果数据包量不大&#xff0c;抓包本身也不大&#xff0c;建议从绝对时间判断&#xff0c;打开wireshark软件&#xff0c;并点开相应要分析的抓包文件。 进入到最上方菜单<视图>,在弹出菜单选择时间显示格式&#xff0c;再在右侧菜单中选择自捕获经过的秒数。 这样就可…

jvm内存优化方式

1. JVM&#xff08;Java Virtual Machine&#xff09;&#xff1a; • 定义&#xff1a;Java虚拟机&#xff0c;是运行Java字节码的抽象计算机。 • 内存管理&#xff1a;负责内存的分配和回收&#xff0c;是JVM内存优化的核心。 2. 堆&#xff08;Heap&#xff09;&#xff1a…

【AI日记】24.12.13 kaggle 比赛 2-3 大扫除、断舍离、自己做饭

【AI论文解读】【AI知识点】【AI小项目】【AI战略思考】【AI日记】 工作 参加&#xff1a;kaggle 比赛 Regression with an Insurance Dataset参考&#xff1a;kaggle 回归类入门比赛 House Prices - Advanced Regression Techniques内容&#xff1a;构建自己的EDA&#xff08…

antdv-<a-button>中属性的使用

UI组件库&#xff08;User Interface Component Library&#xff09;是一种预先构建好的、可重用的用户界面元素集合&#xff0c;旨在帮助开发者更快速、更简便地构建用户界面。这些组件通常包括按钮、表单、导航栏、模态框等&#xff0c;能够提供一致的外观和交互风格&#xf…

Nodejs架构

Node.js 是一个开源的 JavaScript 运行时环境&#xff0c;旨在运行可扩展的应用程序。 Node.js 允许开发人员使用 JavaScript 编写服务器端脚本代码。此外&#xff0c;Node.js 具有能够异步 I/O 的事件驱动架构。它基于 Google Chrome 的 V8 引擎构建&#xff0c;用于开发I/O 密…

对于《穿越火线》和《欢乐升级》游戏的理解

对于《穿越火线》的理解与感受 《穿越火线》&#xff08;CrossFire&#xff09;是一款承载了许多玩家青春记忆的经典FPS游戏。在初次接触这款游戏时&#xff0c;它给我的第一感觉是紧张刺激且极具沉浸感。无论是团队竞技的快节奏对抗&#xff0c;还是爆破模式中步步为营的策略…

Vite快速构建Vue教程

步骤 1: 初始化项目目录 创建一个名为 projects 的文件夹&#xff0c;作为存放所有 Vite 项目的根目录。这个文件夹将容纳多个独立的 Vite 项目。 步骤 2: 创建 Vite 项目 右键点击 projects 文件夹并选择“在此处打开终端”或使用您偏好的代码编辑器&#xff08;如 VSCode&…

springboot429校运会管理系统(论文+源码)_kaic

摘 要 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff0c;在计算机上安装校运会管理系统软件来发挥其高效地信息处理的作用&#xff…

深度学习实验十四 循环神经网络(1)——测试简单循环网络的记忆能力和梯度爆炸实验

目录 一、数据集构建 1.1数据集的构建函数 1.2加载数据集并划分 1.3 构建Dataset类 二、模型构建 2.1嵌入层 2.2SRN层 2.3模型汇总 三、模型训练 3.1 训练指定长度的数字预测模型 3.2 损失曲线展示 四、模型评价 五、修改 附完整可运行代码 实验大体步骤&#x…

SQL去重查询C++ 中面向对象编程如何实现数据隐藏?C++ 中面向对象编程如何处理异常?Pimpl模式

SQL2 查询多列 select device_id,gender,age,university from user_profile; SQL3 查询结果去重 select distinct university from user_profile; select university from user_profile group by university; C 中面向对象编程如何实现数据隐藏&#xff1f; 使用访问控制修饰…

基于物联网的 AI 智能送药车与自维护基站系统研究

一、引言 &#xff08;一&#xff09;研究背景 随着科技的飞速发展&#xff0c;物联网技术在各个领域都展现出了巨大的潜力。在医疗领域&#xff0c;物联网技术的应用为提高医疗服务的效率和质量带来了新的机遇。其中&#xff0c;基于物联网的 AI 智能送药车与自维护基站系统…