Java二十三种设计模式-装饰器模式(7/23)

news2024/9/20 22:31:39

装饰器模式:动态扩展功能的灵活之选

引言

装饰器模式(Decorator Pattern)是一种结构型设计模式,用于在不修改对象自身的基础上,通过添加额外的职责来扩展对象的功能。

基础知识,java设计模式总体来说设计模式分为三大类:

(1)创建型模式,共5种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

(2)结构型模式,共7种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

(3)行为型模式,共11种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

第一部分:装饰器模式概述

1.1 定义与用途

装饰器模式的基本定义

装饰器模式是一种设计模式,允许用户在不修改对象自身的情况下,向一个对象添加新的功能。这种模式通过创建一个包装对象,也就是装饰器,来包裹实际对象,从而在不修改实际对象的基础上扩展其功能。

解释为何需要装饰器模式

  • 动态添加职责:装饰器模式可以在运行时动态地给对象添加额外的职责。
  • 保持对象的透明性:使用装饰器模式不改变对象的接口,因此可以在任何使用原始对象的地方使用装饰过的对象。
  • 避免继承的复杂性:相比通过继承来扩展功能,装饰器模式提供了一种更灵活的替代方案,避免了多重继承的问题。

1.2 装饰器模式的组成

抽象组件(Component)

  • 定义:定义了一个对象接口,可以给这些对象动态地添加职责。
  • 角色:充当具体组件和所有装饰器的公共接口。

具体组件(Concrete Component)

  • 定义:定义了抽象组件的具体实现,也就是被装饰的具体对象。
  • 角色:提供了具体的行为和数据。

抽象装饰器(Decorator)

  • 定义:抽象组件的子类,实现了与抽象组件相同的接口,并持有一个抽象组件的实例。
  • 角色:为组件添加职责提供了方法,同时保持接口不变。

具体装饰器(Concrete Decorator)

  • 定义:抽象装饰器的子类,实现具体装饰功能。
  • 角色:通过实现抽象装饰器的接口,给具体组件添加额外的职责。

客户端(Client)

  • 角色:使用抽象组件接口与具体组件和装饰器交互。
  • 职责:客户端并不关心对象是具体组件还是装饰器,它通过相同的接口与所有对象交互。

装饰器模式通过将一个对象包装在装饰器对象中来扩展功能,而不需要修改对象的类代码。这种模式提供了一种灵活的方式来扩展或增强对象的行为。在下一部分中,我们将通过Java代码示例来展示装饰器模式的具体实现。

第二部分:装饰器模式的实现

2.1 Java实现示例

以下是使用Java语言实现装饰器模式的一个示例。假设我们有一个简单的咖啡类,我们想要通过装饰器模式来添加不同的调料和装饰。

// 抽象组件
interface Coffee {
    double cost();
    String getIngredients();
}

// 具体组件
class SimpleCoffee implements Coffee {
    @Override
    public double cost() {
        return 10.0;
    }

    @Override
    public String getIngredients() {
        return "Simple Coffee";
    }
}

// 抽象装饰器
abstract class CoffeeDecorator implements Coffee {
    protected Coffee decoratedCoffee;

    public CoffeeDecorator(Coffee coffee) {
        this.decoratedCoffee = coffee;
    }

    @Override
    public double cost() {
        return decoratedCoffee.cost();
    }

    @Override
    public String getIngredients() {
        return decoratedCoffee.getIngredients();
    }
}

// 具体装饰器
class Milk extends CoffeeDecorator {
    public Milk(Coffee coffee) {
        super(coffee);
    }

    @Override
    public double cost() {
        return super.cost() + 2.0;
    }

    @Override
    public String getIngredients() {
        return super.getIngredients() + ", Milk";
    }
}

class Whip extends CoffeeDecorator {
    public Whip(Coffee coffee) {
        super(coffee);
    }

    @Override
    public double cost() {
        return super.cost() + 1.5;
    }

    @Override
    public String getIngredients() {
        return super.getIngredients() + ", Whip";
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        Coffee coffee = new SimpleCoffee();
        System.out.println("Cost: " + coffee.cost());
        System.out.println("Ingredients: " + coffee.getIngredients());

        Coffee milkCoffee = new Milk(coffee);
        System.out.println("Cost: " + milkCoffee.cost());
        System.out.println("Ingredients: " + milkCoffee.getIngredients());

        Coffee whipCoffee = new Whip(milkCoffee);
        System.out.println("Cost: " + whipCoffee.cost());
        System.out.println("Ingredients: " + whipCoffee.getIngredients());
    }
}

在这个示例中,Coffee是抽象组件,SimpleCoffee是具体组件。CoffeeDecorator是抽象装饰器,MilkWhip是具体装饰器。客户端可以通过装饰器来动态地添加调料和装饰。

2.2 装饰器模式中的角色和职责

抽象组件(Component)

  • 职责:定义装饰对象和具体对象的公共接口,允许通过装饰器来扩展功能。

具体组件(Concrete Component)

  • 职责:定义一个具体的类,实现抽象组件接口,是装饰器装饰的对象。

抽象装饰器(Decorator)

  • 职责:持有一个组件(Component)对象的实例,定义装饰器的接口,继承自抽象组件。
  • 实现:通常是一个抽象类,实现接口中的方法,并将调用委托给被装饰的组件。

具体装饰器(Concrete Decorator)

  • 职责:实现抽象装饰器的接口,添加装饰的功能。
  • 实现:具体装饰器类会持有一个组件对象,并在调用组件方法前后添加额外的行为。

客户端(Client)

  • 职责:通过抽象组件接口与具体组件和装饰器交互,客户端不关心对象是具体组件还是装饰器。

装饰器模式通过在运行时动态地添加装饰器来扩展对象的功能,提供了一种灵活且强大的方式来增强对象的行为。在下一部分中,我们将探讨装饰器模式的使用场景。

第三部分:装饰器模式的使用场景

3.1 动态添加职责

在软件开发中,经常会遇到需要在运行时动态地给对象添加额外职责的情况。装饰器模式提供了一种灵活的方式来实现这一点,而无需改变对象本身的结构。

何时需要动态地给对象添加职责:

  • 功能扩展:当需要为对象添加新功能,但又不想通过继承来扩展现有类时。
  • 条件性行为:某些行为只在特定条件下需要,装饰器模式可以根据运行时的条件来决定是否添加这些行为。
  • 多样化组合:需要多种行为组合时,装饰器模式允许通过组合不同的装饰器来实现。

应用实例:

  • GUI组件:在图形用户界面中,可能需要为组件动态添加如边框、颜色、阴影等视觉效果。
  • 日志记录:在不修改现有方法的情况下,为方法动态添加日志记录功能。

3.2 避免使用多重继承

Java等许多编程语言不支持多重继承,这限制了通过继承来扩展对象功能的方式。装饰器模式提供了一种替代方案,允许一个对象在运行时拥有多个“继承”自不同类的行为。

在需要避免多重继承的情况下,装饰器模式的优势:

  • 解决多重继承问题:装饰器模式允许对象在运行时拥有多个职责,而不需要通过多重继承实现。
  • 灵活性:装饰器模式提供了更高的灵活性,可以在运行时动态地添加或移除职责。
  • 保持类的独立性:避免了因为多重继承导致的类之间的强耦合。

应用实例:

  • 支付系统:在一个支付系统中,可能需要为支付方法添加日志记录、事务管理、安全性检查等多种功能。使用装饰器模式可以避免创建过多的继承层级。
  • 网络通信:在网络通信中,可能需要为通信协议添加加密、压缩、认证等多种功能。装饰器模式允许在不修改协议本身的情况下,动态地添加这些功能。

装饰器模式是一种非常有用的设计模式,它允许开发者在不修改对象结构的前提下,动态地扩展对象的功能。在实际应用中,根据具体需求和场景选择是否使用装饰器模式是非常重要的。在下一部分中,我们将讨论装饰器模式的优点与缺点。

第四部分:装饰器模式的优点与缺点

4.1 优点

提高灵活性

  • 动态添加行为:装饰器模式可以在运行时动态地给对象添加额外的行为或职责。

增强可扩展性

  • 无需修改原有代码:通过装饰器可以扩展功能,而无需修改原有的类代码,遵循开闭原则。

简化对象复用

  • 减少类的数量:避免创建多个继承层次,减少系统中类的数量,简化复用。

提供更好的封装性

  • 隐藏实现细节:装饰器模式将对象的具体实现细节封装在装饰器内部,对外提供统一的接口。

4.2 缺点

过度使用导致复杂性

  • 多层装饰器嵌套:如果过度使用装饰器模式,可能会导致多层嵌套,难以理解和维护。

增加系统的复杂度

  • 类结构复杂:装饰器模式可能会使系统的类结构变得更加复杂,尤其是当有多个装饰器时。

性能问题

  • 可能影响性能:在某些情况下,装饰器模式可能会引入额外的性能开销,尤其是在装饰器较多的情况下。

第五部分:装饰器模式与其他模式的比较

5.1 与适配器模式的比较

适配器模式

  • 目的:使不兼容的接口能够一起工作。
  • 实现:通常通过继承或组合来转换一个类的接口。

装饰器模式

  • 目的:动态地给对象添加额外的职责。
  • 实现:通过组合来包装对象,增加新的行为。

对比

  • 目的不同:适配器模式主要用于接口转换,而装饰器模式用于功能扩展。
  • 使用场景:适配器模式适用于解决接口不兼容的问题,装饰器模式适用于动态添加职责。

5.2 与外观模式的对比

外观模式

  • 目的:提供一个统一的接口来访问子系统中的一组接口。
  • 实现:通过一个外观类来简化客户端与复杂系统的交互。

装饰器模式

  • 目的:动态地给对象添加额外的职责。

对比

  • 简化接口:外观模式用于简化客户端与复杂系统之间的接口,而装饰器模式用于在不修改对象的情况下扩展功能。
  • 使用场景:外观模式适用于简化系统访问,装饰器模式适用于功能增强。

装饰器模式提供了一种灵活的方式来动态地扩展对象的功能,但也需要谨慎使用,以避免增加系统的复杂性和维护难度。在实际应用中,根据具体需求和场景选择合适的设计模式是非常重要的。在下一部分中,我们将提供装饰器模式的最佳实践和建议。

第六部分:装饰器模式的最佳实践和建议

6.1 最佳实践

保持接口的一致性

  • 统一接口:确保所有装饰器和被装饰的组件都实现同一个接口,这样客户端就可以以统一的方式处理它们。

装饰者应该是非侵入性的

  • 不修改原始类:装饰者不应该修改原始类代码,以保证原始类的独立性和可重用性。

单一职责原则

  • 单一职责:每个装饰者应该只关注添加一种职责,遵循单一职责原则。

使用装饰者工厂

  • 工厂模式:使用工厂模式来创建装饰者,这样可以更好地控制对象的创建过程。

避免过度装饰

  • 适度使用:避免创建过多的装饰者,以免系统变得复杂难以管理。

6.2 避免滥用

避免多层装饰器导致的复杂性

  • 简化设计:避免不必要的多层嵌套装饰,保持设计的简洁性。

避免过度复杂的继承结构

  • 扁平化设计:尽量保持扁平化的设计,避免创建过多的装饰者类。

考虑使用组合而非继承

  • 组合优于继承:在某些情况下,使用对象组合代替继承可能更灵活。

6.3 替代方案

#.# 使用组合模式

  • 定义:组合模式允许将对象组合成树形结构,以表示“部分-整体”的层次结构。
  • 适用场景:当需要表示对象之间的层次关系时。

使用策略模式

  • 定义:策略模式定义了一系列算法,并将每一个算法封装起来,使它们可以互换。
  • 适用场景:当需要根据不同的策略动态改变对象行为时。

使用外观模式

  • 适用场景:当需要简化客户端对复杂系统的访问时。

使用享元模式

  • 定义:享元模式通过共享来高效地支持大量细粒度对象的复用。
  • 适用场景:当系统中存在大量相似对象时,可以通过享元模式来减少内存消耗。

装饰器模式是一种强大的设计模式,可以提供灵活性和可扩展性,但也需要谨慎使用以避免复杂性和性能问题。了解其替代方案可以帮助开发者根据具体需求和场景选择最合适的设计模式。在实际开发中,应根据具体情况灵活运用装饰器模式,以达到最佳的设计效果。

结语

装饰器模式提供了一种灵活的方式来扩展对象的功能,而无需改变对象的结构。通过本文的深入分析,希望读者能够对装饰器模式有更全面的理解,并在实际开发中做出合理的设计选择。

相关Java设计模式文章:

Java二十三种设计模式-单例模式(1/23)

Java二十三种设计模式-工厂方法模式(2/23)

Java二十三种设计模式-抽象工厂模式(3/23)

Java二十三种设计模式-建造者模式(4/23)

Java二十三种设计模式-原型模式(5/23)

Java二十三种设计模式-适配器模式(6/23)

Java二十三种设计模式-装饰器模式(7/23)

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

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

相关文章

MT19937

MT19937 文章目录 MT19937题型1 逆向extract_number[SUCTF2019]MT 题型2 预测随机数[GKCTF 2021]Random 题型3逆向twist[V&N2020 公开赛]Backtrace 题型4 逆向init扩展题型WKCTF easy_random 现成模块randcrack库Extend MT19937 Predictor库 MT19937是一种周期很长的伪随机…

【C++】深刻基础笔记

目录 关于debug&#xff1a; 多文件编译&#xff1a; 编译器工作原理 预处理&#xff1a; 如何将机器二进制转换成可以看懂的文件 链接器的工作原理 Pointers指针 Reference引用 C线程 程序如何从源文件变成exe可执行文件&#xff1f; 首先是预处理器#include <..…

斐波那契数列的多种解法 C++实现,绘图部分用Python实现

斐波那契数列的多种解法 C实现&#xff0c;绘图部分用Python实现 flyfish 斐波那契数列&#xff08;Fibonacci sequence&#xff09;是一个经典的数列&#xff0c;定义如下&#xff1a; { 0 if n 0 1 if n 1 F ( n − 1 ) F ( n − 2 ) if n > 1 \begin{cases} 0 &…

c++网络编程实战——开发基于ftp协议的文件传输模块(二) 配置ftp服务与手动执行ftp命令

配置FTP服务 一.前言 博主的环境是阿里云服务器&#xff0c;操作系统版本为 ubuntu20.04,一下所有操作都基于以上环境下进行的操作&#xff0c;同时为了简化操作我将开放同一个云服务器的不同端口&#xff0c;让它同时充当服务端和客户端&#xff0c;大家如果想测试效果更好且…

Java基础:类与对象,递归,方法

类与对象的区别和联系 1.类是抽象的,概念的他是数据类型 2.对象是具体的实际的,代表一个具体事务 3.类是对象的模板,对象是类的个体 **对象在内存中的存在形式** 基本数据类型在堆,引用类型如String,数组在方法区 对象的属性 属性基本说明 1.成员变量or成员属性 属性的定…

双向链表<数据结构 C版>

目录 关于链表的分类 双向链表结构体 初始化 尾插 头插 打印 判断是否为空 尾删 头删 查找 指定位置之后的插入 指定位置的删除 销毁 关于链表的分类 根据链表的三大特性&#xff0c;单向or双向、带头or不带头、循环or不循环&#xff0c;可将链表分为2*2*2&#xf…

利用ascp下载SRA Explorer中转录组数据

最近在windows系统里下载了MobaXterm可以远程登入服务器&#xff0c;处理RNA的数据&#xff0c;需要从NCBI数据库上下载数据。本文提供用虚拟机ubuntu或者linux系统下载Aspera的方法和问题解决&#xff0c;以及从NCBI上批量下载数据库、最后得到一个项目里的所有fastq文件。 A…

前端:Vue学习-2

前端&#xff1a;Vue学习-2 1. vue的生命周期2. 工程化开发和脚手架Vue CLI2.1 组件化开发2.2 scoped解决样式冲突2.3 data是一个函数2.4 组件通信2.5 非父子通信- event bus事件&#xff0c;provide&inject 3.v-model原理->实现父子组件双向绑定4. sync 修饰符->实现…

谷粒商城实战笔记-42-前端基础-Vue-生命周期和钩子函数

下面是Vue官网关于生命周期及不同阶段的钩子函数的图示。 Vue 实例生命周期是指从一个组件被创建到最终被销毁的整个过程。 在这一过程中&#xff0c;Vue 提供了一系列的“钩子”函数&#xff0c;在生命周期的不同阶段执行自定义的代码。 以下是 Vue 对象生命周期的主要阶段…

C语言实现二叉树以及二叉树的详细介绍

目录 1.树概念及结构 1.1树的概念 1.2树的相关概念 1.3树的表示 2.二叉树概念及结构 2.1二叉树的概念 2.2特殊的二叉树 2.3二叉树的性质 2.4二叉树的存储结构 3.二叉树顺序结构--特殊的二叉树--堆及其实现 3.1堆的概念及结构 3.2堆的实现 3.2.1堆的结构 3.2.2堆…

filebeat生产环境配置

配置文件属性 生产配置 filebeat.inputs: - type: logenabled: truepaths: - /tmp/logs/*.log- /var/log/system.log- /var/log/wifi.logsymlinks: truejson.keys_under_root: truejson.message_key: xxxjson.add_error_key: true# 如果想卡部分日志&#xff0c;比如用时间作…

Monaco 使用 HoverProvider

Monaco 中自定义 Hover&#xff0c;Hover 效果是指当鼠标移动文字上展示出提示效果&#xff0c;就像页面上的 Tooltip 效果。最终页面的显示效果如下&#xff1a; 通过 registerHoverProvider 注册 Hover 触发时的处理方法 接口中提供了 4 个参数&#xff0c;前两个参数比较重…

python学习之闭包与装饰器

一、闭包 闭包允许一个函数访问并操作函数外部的变量&#xff08;即父级作用域中的变量&#xff09;&#xff0c;即使在该函数外部执行。 特性&#xff1a; (1)外部函数嵌套内部函数。 (2)外部函数可以返回内部函数。 (3)内部函数可以访问外部函数的局部变量。 def out()…

【jmeter边界值提取器】

【目的】 从响应头中取token的内容 【方案】 使用后置处理器-边界值提取器 【组件路径】HTTP请求->右键添加->后置处理器->边界提取器 用途&#xff1a;边界提取器(Boundary Extractor)是放在Sample请求之后执行的&#xff0c;用于获取左右边界中间的所有字符&#xf…

Windows配置Qt+VLC

文章目录 前言下载库文件提取文件编写qmakeqtvlc测试代码 总结 前言 在Windows平台上配置Qt和VLC是开发多媒体应用程序的一个重要步骤。Qt作为一个强大的跨平台应用开发框架&#xff0c;为开发人员提供了丰富的GUI工具和库&#xff0c;而VLC则是一个开源的多媒体播放器&#x…

基于STM32的农业大棚温湿度采集控制系统的设计

目录 1、设计要求 2、系统功能 3、演示视频和实物 4、系统设计框图 5、软件设计流程图 6、原理图 7、主程序 8、总结 &#x1f91e;大家好&#xff0c;这里是5132单片机毕设设计项目分享&#xff0c;今天给大家分享的是智能教室。 设备的详细功能见网盘中的文章《8、基…

451.根据字符出现频率排序(中等)

451.根据字符出现频率排序&#xff08;中等&#xff09; 1. 题目描述2.详细题解3.代码实现3.1 Python3.2 Java 1. 题目描述 题目中转&#xff1a;451.根据字符出现频率排序&#xff08;中等&#xff09; 2.详细题解 题目&#xff1a; 347. 前 K 个高频元素&#xff08;中等&am…

2月科研——arcgis计算植被差异

ArcGIS中&#xff0c;设置高于或低于某个值的像元为 -9999&#xff0c;然后将这些地方设为空——目的&#xff1a;去除异常值和黑色背景值 Con(("T_std ano7.tif" > 2) | ("T_std ano7.tif" < - 2), - 9999,"T_std ano7.tif") SetNull(&…

数学基础【俗说矩阵】:初等矩阵和矩阵的初等行变化关系推导

初等矩阵和矩阵的初等行变换 初等矩阵 矩阵的初等行变换 对单位阵E进行一次初等行变化得到的阵叫做初等阵。 这里只能进行一次初等行变换。 置换阵 给矩阵【左乘】一个【置换阵】&#xff0c;相当与对该矩阵进行了一次【置换阵】对应的【置换】初等行变换&#xff1b; 数…

爬取百度图片,想爬谁就爬谁

前言 既然是做爬虫&#xff0c;那么肯定就会有一些小心思&#xff0c;比如去获取一些自己喜欢的资料等。 去百度图片去抓取图片吧 打开百度图片网站&#xff0c;点击搜索xxx&#xff0c;打开后&#xff0c;滚动滚动条&#xff0c;发现滚动条越来越小&#xff0c;说明图片加载…