【设计模式】装饰(器)模式 透明装饰模式与半透明装饰模式
装饰模式(Decorator Pattern)详解
一、装饰模式简介
装饰模式(Decorator Pattern) 是一种 结构型设计模式,它允许你动态地给对象添加行为或职责,而无需修改其源代码,也不需要使用继承来扩展功能。
是一种用于替代继承的技术,它通过一种无须定义子类的方式给对象动态增加职责,使用对象之间的关联关系取代类之间的继承关系
引入了装饰类,在装饰类中既可以调用待装饰的原有类的方法,还可以增加新的方法,以扩展原有类的功能
你可以把它理解为“在不改变原对象的前提下,为其添加新功能的方式”。与继承不同的是,装饰模式是运行时动态组合的,更加灵活。
动态地给一个对象增加一些额外的职责。就扩展功能而言,装饰模式提供了一种比使用子类更加灵活的替代方案。
以对客户透明的方式动态地给一个对象附加上更多的责任
可以在不需要创建更多子类的情况下,让对象的功能得以扩展
装饰模式的结构
装饰模式包含以下4个角色:
Component(抽象构件)
ConcreteComponent(具体构件)
Decorator(抽象装饰类)
ConcreteDecorator(具体装饰类)
二、解决的问题类型
装饰模式主要用于解决以下问题:
- 当子类扩展不再适用:比如当你有多个可选功能,并且这些功能可以自由组合时,如果用继承方式实现,会导致类爆炸。
- 希望以透明和动态的方式给对象添加职责:不需要修改客户端代码,就可以为对象添加新的功能。
- 保持类责任清晰:每个装饰器只关注一个功能,符合单一职责原则。
三、使用场景
- 你想在不影响其他对象的情况下,动态、透明地给某个对象添加职责。
- 你需要在运行时决定是否添加某个功能,甚至多次添加多个功能。
- 替代多重继承的方案:避免由于多层继承导致的类爆炸问题。
- Java I/O 流系统中广泛使用装饰者模式,如
BufferedInputStream
包裹FileInputStream
。 - 当不能采用继承的方式对系统进行扩展或者采用继承不利于系统扩展和维护时可以使用装饰模式
四、实际生活案例
想象你在点一杯咖啡:
- 基础咖啡:美式咖啡(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
抽象构件类典型代码(c++)
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() { //功能扩展}
}
其他案例
- 某软件公司基于面向对象技术开发了一套图形界面构件库——VisualComponent,该构件库提供了大量基本构件,如窗体、文本框、列表框等,由于在使用该构件库时,用户经常要求定制一些特殊的显示效果,如带滚动条的窗体、带黑色边框的文本框、既带滚动条又带黑色边框的列表框等等,因此经常需要对该构件库进行扩展以增强其功能。
现使用装饰模式来设计该图形界面构件库。
- 变形金刚:变形金刚在变形之前是一辆汽车,它可以在陆地上移动。当它变成机器人之后除了能够在陆地上移动之外,还可以说话;如果需要,它还可以变成飞机,除了在陆地上移动还可以在天空中飞翔。
- 多重加密系统:
某系统提供了一个数据加密功能,可以对字符串进行加密。最简单的加密算法通过对字母进行移位来实现,同时还提供了稍复杂的逆向输出加密,还提供了更为高级的求模加密。用户先使用最简单的加密算法对字符串进行加密,如果觉得还不够可以对加密之后的结果使用其他加密算法进行二次加密,当然也可以进行第三次加密。现使用装饰模式设计该多重加密系统
六、优缺点分析
优点 | 描述 |
---|---|
✅ 比继承更灵活 | 可在运行时动态添加功能,避免了类爆炸问题。可以通过一种动态的方式来扩展一个对象的功能,通过配置文件可以在运行时选择不同的具体装饰类,从而实现不同的行为 |
✅ 遵循开闭原则 | 扩展功能时不需要修改原有代码。具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,且原有类库代码无须改变,符合开闭原则 |
✅ 职责分离清晰 | 每个装饰器只负责一个功能,便于维护和复用。可以对一个对象进行多次装饰 |
缺点 | 描述 |
---|---|
❌ 增加系统复杂度 | 多个装饰器嵌套后可能让代码难以理解和调试。使用装饰模式进行系统设计时将产生很多小对象,大量小对象的产生势必会占用更多的系统资源,在一定程度上影响程序的性能 |
❌ 调试困难 | 对象层层包装,追踪执行路径较为麻烦。比继承更加易于出错,排错也更困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐 |
❌ 容易误用装饰顺序 | 某些情况下装饰顺序会影响最终效果,需特别注意。 |
七、与其他模式对比(补充)
模式名称 | 目标 |
---|---|
代理模式 | 控制对对象的访问,强调保护或增强,但不改变接口。 |
适配器模式 | 解决接口不兼容问题,强调转换。 |
装饰模式 | 动态增强对象功能,强调组合和扩展。 |
八、最终小结
装饰模式是一种非常实用的设计模式,尤其适合用于那些需要在不修改现有对象的前提下动态扩展其功能的场景。
它的核心思想是:
“组合优于继承”,“透明地增强对象能力”。
在 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大模型生成,注意识别!