当前位置: 首页 > news >正文

【设计模式】装饰(器)模式 透明装饰模式与半透明装饰模式

装饰模式(Decorator Pattern)详解


一、装饰模式简介

装饰模式(Decorator Pattern) 是一种 结构型设计模式,它允许你动态地给对象添加行为或职责,而无需修改其源代码,也不需要使用继承来扩展功能。

是一种用于替代继承的技术,它通过一种无须定义子类的方式给对象动态增加职责,使用对象之间的关联关系取代类之间的继承关系

引入了装饰类,在装饰类中既可以调用待装饰的原有类的方法,还可以增加新的方法,以扩展原有类的功能

你可以把它理解为“在不改变原对象的前提下,为其添加新功能的方式”。与继承不同的是,装饰模式是运行时动态组合的,更加灵活。

动态地给一个对象增加一些额外的职责。就扩展功能而言,装饰模式提供了一种比使用子类更加灵活的替代方案

以对客户透明的方式动态地给一个对象附加上更多的责任
可以在不需要创建更多子类的情况下,让对象的功能得以扩展

装饰模式的结构

装饰模式包含以下4个角色:

Component(抽象构件)
ConcreteComponent(具体构件)
Decorator(抽象装饰类)
ConcreteDecorator(具体装饰类)

在这里插入图片描述


二、解决的问题类型

装饰模式主要用于解决以下问题:

  • 当子类扩展不再适用:比如当你有多个可选功能,并且这些功能可以自由组合时,如果用继承方式实现,会导致类爆炸。
  • 希望以透明和动态的方式给对象添加职责:不需要修改客户端代码,就可以为对象添加新的功能。
  • 保持类责任清晰:每个装饰器只关注一个功能,符合单一职责原则。

三、使用场景

  1. 你想在不影响其他对象的情况下,动态、透明地给某个对象添加职责。
  2. 你需要在运行时决定是否添加某个功能,甚至多次添加多个功能。
  3. 替代多重继承的方案:避免由于多层继承导致的类爆炸问题。
  4. Java I/O 流系统中广泛使用装饰者模式,如 BufferedInputStream 包裹 FileInputStream
  5. 不能采用继承的方式对系统进行扩展或者采用继承不利于系统扩展和维护时可以使用装饰模式

四、实际生活案例

想象你在点一杯咖啡:

  • 基础咖啡:美式咖啡(Espresso)
  • 可选配料:牛奶(Milk)、摩卡(Mocha)、奶油(Whip)

每种配料都可以看作是对咖啡的“装饰”,你可以选择加一样,也可以叠加多种,最终价格也会随之变化。

这种“基础 + 动态添加”的场景非常适合使用装饰模式。


五、代码案例

我们通过一个简单的咖啡加料的例子来演示装饰模式。

1. 定义组件接口

// 组件接口:饮料
interface Beverage {double cost();     // 价格String description(); // 描述
}

2. 实现具体组件(基础对象)

// 具体组件:美式咖啡
class Espresso implements Beverage {@Overridepublic double cost() {return 2.0;}@Overridepublic String description() {return "Espresso";}
}

3. 抽象装饰器类(所有装饰器的基础)

// 抽象装饰器类
abstract class BeverageDecorator implements Beverage {protected Beverage decoratedBeverage;public BeverageDecorator(Beverage decoratedBeverage) {this.decoratedBeverage = decoratedBeverage;}@Overridepublic double cost() {return decoratedBeverage.cost();}@Overridepublic String description() {return decoratedBeverage.description();}
}

4. 具体装饰器类(装饰行为)

// 加牛奶的装饰器
class MilkDecorator extends BeverageDecorator {public MilkDecorator(Beverage decoratedBeverage) {super(decoratedBeverage);}@Overridepublic double cost() {return super.cost() + 0.5;}@Overridepublic String description() {return super.description() + ", Milk";}
}// 加摩卡的装饰器
class MochaDecorator extends BeverageDecorator {public MochaDecorator(Beverage decoratedBeverage) {super(decoratedBeverage);}@Overridepublic double cost() {return super.cost() + 0.7;}@Overridepublic String description() {return super.description() + ", Mocha";}
}// 加奶油的装饰器
class WhipDecorator extends BeverageDecorator {public WhipDecorator(Beverage decoratedBeverage) {super(decoratedBeverage);}@Overridepublic double cost() {return super.cost() + 0.6;}@Overridepublic String description() {return super.description() + ", Whip";}
}

5. 客户端调用示例

public class Client {public static void main(String[] args) {// 点一杯加牛奶和摩卡的咖啡Beverage beverage = new Espresso();beverage = new MilkDecorator(beverage);beverage = new MochaDecorator(beverage);System.out.println("Cost: $" + beverage.cost());System.out.println("Description: " + beverage.description());// 再加奶油beverage = new WhipDecorator(beverage);System.out.println("\nAfter adding whip:");System.out.println("Cost: $" + beverage.cost());System.out.println("Description: " + beverage.description());}
}

输出结果:

Cost: $3.2
Description: Espresso, Milk, MochaAfter adding whip:
Cost: $3.8
Description: Espresso, Milk, Mocha, Whip
抽象构件类典型代码
abstract class Component
{public abstract void Operation();
}
具体构件类典型代码
class ConcreteComponent : Component
{public override void Operation(){//基本功能实现}
}
抽象装饰类典型代码
class Decorator : Component
{
private Component component;  //维持一个对抽象构件对象的引用//注入一个抽象构件类型的对象public Decorator(Component component){this.component = component;}public override void Operation(){component.Operation();  //调用原有业务方法}
}
具体装饰类典型代码
class ConcreteDecorator : Decorator
{public ConcreteDecorator(Component component) : base(component){}public override void Operation() {base.Operation();  //调用原有业务方法AddedBehavior();  //调用新增业务方法}
//新增业务方法    
public void AddedBehavior() {	//功能扩展}
}
其他案例
  1. 某软件公司基于面向对象技术开发了一套图形界面构件库——VisualComponent,该构件库提供了大量基本构件,如窗体、文本框、列表框等,由于在使用该构件库时,用户经常要求定制一些特殊的显示效果,如带滚动条的窗体、带黑色边框的文本框、既带滚动条又带黑色边框的列表框等等,因此经常需要对该构件库进行扩展以增强其功能。
    现使用装饰模式来设计该图形界面构件库。
    在这里插入图片描述
  2. 变形金刚:变形金刚在变形之前是一辆汽车,它可以在陆地上移动。当它变成机器人之后除了能够在陆地上移动之外,还可以说话;如果需要,它还可以变成飞机,除了在陆地上移动还可以在天空中飞翔。
    在这里插入图片描述
  3. 多重加密系统:
    某系统提供了一个数据加密功能,可以对字符串进行加密。最简单的加密算法通过对字母进行移位来实现,同时还提供了稍复杂的逆向输出加密,还提供了更为高级的求模加密。用户先使用最简单的加密算法对字符串进行加密,如果觉得还不够可以对加密之后的结果使用其他加密算法进行二次加密,当然也可以进行第三次加密。现使用装饰模式设计该多重加密系统

在这里插入图片描述


六、优缺点分析

优点描述
比继承更灵活可在运行时动态添加功能,避免了类爆炸问题。可以通过一种动态的方式来扩展一个对象的功能,通过配置文件可以在运行时选择不同的具体装饰类,从而实现不同的行为
遵循开闭原则扩展功能时不需要修改原有代码。具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,且原有类库代码无须改变,符合开闭原则
职责分离清晰每个装饰器只负责一个功能,便于维护和复用。可以对一个对象进行多次装饰
缺点描述
增加系统复杂度多个装饰器嵌套后可能让代码难以理解和调试。使用装饰模式进行系统设计时将产生很多小对象,大量小对象的产生势必会占用更多的系统资源,在一定程度上影响程序的性能
调试困难对象层层包装,追踪执行路径较为麻烦。比继承更加易于出错,排错也更困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐
容易误用装饰顺序某些情况下装饰顺序会影响最终效果,需特别注意。

七、与其他模式对比(补充)

模式名称目标
代理模式控制对对象的访问,强调保护或增强,但不改变接口。
适配器模式解决接口不兼容问题,强调转换。
装饰模式动态增强对象功能,强调组合和扩展。

八、最终小结

装饰模式是一种非常实用的设计模式,尤其适合用于那些需要在不修改现有对象的前提下动态扩展其功能的场景。

它的核心思想是:

组合优于继承”,“透明地增强对象能力”。

在 Java 中,最经典的使用就是 IO 流体系,例如 BufferedReader(new InputStreamReader(System.in)),就是一个典型的装饰链。

作为一名 Java 开发工程师,掌握装饰模式可以帮助你写出更优雅、更灵活、更容易扩展的代码,特别是在开发插件化、模块化系统时尤为重要。

如果你正在设计一个需要支持“插件式”功能扩展的系统,或者希望避免因继承带来的类爆炸问题,那么装饰模式将是一个非常好的选择。


📌 一句话总结:

装饰模式就像“洋葱”,一层层包裹原始对象,在不破坏原有结构的前提下,动态赋予新功能。

九、扩展

透明装饰模式

透明(Transparent)装饰模式:要求客户端完全针对抽象编程,装饰模式的透明性要求客户端程序不应该将对象声明为具体构件类型或具体装饰类型,而应该全部声明为抽象构件类型。

对于客户端而言,具体构件对象和具体装饰对象没有任何区别。

可以让客户端透明地使用装饰之前的对象和装饰之后的对象,无须关心它们的区别
可以对一个已装饰过的对象进行多次装饰,得到更为复杂、功能更为强大的对象
无法在客户端单独调用新增方法AddedBehavior()

……
Component component_o,component_d1,component_d2; //全部使用抽象构件定义
component_o = new ConcreteComponent();
component_d1 = new ConcreteDecorator1(component_o);
component_d2 = new ConcreteDecorator2(component_d1);
component_d2.Operation();
//无法单独调用component_d2的AddedBehavior()方法
……

半透明装饰模式

半透明(Semi-transparent)装饰模式:用具体装饰类型来定义装饰之后的对象,而具体构件使用抽象构件类型来定义。
对于客户端而言,具体构件类型无须关心,是透明的;但是具体装饰类型必须指定,这是不透明的。

可以给系统带来更多的灵活性,设计相对简单,使用起来也非常方便
客户端使用具体装饰类型来定义装饰后的对象,因此可以单独调用AddedBehavior()方法
最大的缺点在于不能实现对同一个对象的多次装饰,而且客户端需要有区别地对待装饰之前的对象和装饰之后的对象

……
Component component_o; //使用抽象构件类型定义
component_o = new ConcreteComponent();
component_o.Operation();
ConcreteDecorator component_d; //使用具体装饰类型定义
component_d = new ConcreteDecorator(component_o);
component_d.Operation();
component_d.AddedBehavior(); //单独调用新增业务方法
……

以上内容部分由AI大模型生成,注意识别!

http://www.dtcms.com/a/275543.html

相关文章:

  • 前端MQTT入门指南:从零到实战的完整流程
  • Google浏览器【无法安装扩展程序,因为它使用了不受支持的清单版本】解决方案
  • 【FreeRTOS】信号量
  • 自助KTV选址指南与优化策略
  • 刘火良 FreeRTOS内核实现与应用之5——补充知识(宏)
  • [Python] -实用技巧篇1-用一行Python代码搞定日常任务
  • Effective Modern C++ 条款9:优先考虑别名声明而非typedef
  • C++法则21:避免将#include放在命名空间内部。
  • Java-71 深入浅出 RPC Dubbo 上手 父工程配置编写 附详细POM与代码
  • Java使用Langchai4j接入AI大模型的简单使用(一)
  • 【跟我学运维】chkconfig jenkins on的含义
  • 使用 Java 开发大数据应用:Hadoop 与 Java API 的结合
  • Gas and Gas Price
  • MCP选型指南:AWS vs Azure vs GCP vs 国内云厂商深度对比
  • 从 Spring 源码到项目实战:设计模式落地经验与最佳实践
  • 批量自动运行多个 Jupyter Notebook 文件的方法!!!
  • 13. G1垃圾回收器
  • Edge浏览器:报告不安全的站点的解决方案
  • 【字符串移位包含问题】2022-8-7
  • Kotlin文件操作
  • 浅谈 Python 中的 yield——yield的返回值与send()的关系
  • Ether and Wei
  • Spring 框架中的设计模式:从实现到思想的深度解析
  • 贪心算法题解——跳跃游戏【LeetCode】
  • AI大模型(七)Langchain核心模块与实战(二)
  • Android音视频探索之旅 | C++层使用OpenGL ES实现视频渲染
  • CTFHub————Web{信息泄露[Git泄露(log)]}
  • 《Java Web程序设计》实验报告五 Java Script学习汇报
  • Redis Geospatial 功能详解及多边形包含判断实现
  • win10安装Rust Webassembly工具链(wasm-pack)报错。