装饰者模式 (Decorator)
想给你的对象增添新功能?不用修改原对象,装饰者帮你轻松“加装”功能,提升你的对象能力。
有时候,你的对象已经非常完美了,但你又希望在不改变它的情况下,增加一些新的功能。这时,你就需要一个“魔法工具”来让这个对象变得更加多才多艺。装饰者模式正是这种“魔法工具”,它允许你在运行时动态地给对象“加装”额外的功能,而不需要直接修改原对象的代码。就像是给汽车加装新的配件,原本的汽车性能并没有改变,但你可以加装导航、音响、座椅加热等,让它的功能变得更强大。
装饰者模式的精髓就在于:它提供了一种灵活的方式来扩展对象的行为,而不需要创建复杂的子类,也避免了直接修改原有代码带来的风险。它通过将功能包装到现有对象中,让你可以按需添加功能,而且装饰者的组合也十分灵活,可以创建出非常丰富的功能组合。
趣味解读:给你的小汽车加装功能!
想象一下,你买了一辆漂亮的小车,车子本身非常简洁、优雅。可是你发现自己有些新需求:你希望有座椅加热、蓝牙音响、天窗等功能,但你不想更换整辆车。于是你去汽车改装店,选择一个个配件,然后“加装”到你的车上。这些配件本身并不会改变小车的外观或基础功能,它们只会增添额外的特性,让小车变得更强大。
这就是装饰者模式的操作方式:你不需要拆解原有对象,只需要在其外层添加一些装饰类来“增强”功能,最终得到一个功能更全的小车(或者对象)。
Java代码案例:饮料加料
假设你正在开发一个咖啡店的系统,顾客可以选择不同的饮料,而每种饮料都可以根据顾客的需求加上一些配料(如牛奶、糖浆等)。你可以使用装饰者模式来动态地为饮料加料,而不需要修改饮料的原始定义。
// 1. 定义饮料接口
interface Beverage {
double cost(); // 计算饮料的价格
String description(); // 返回饮料描述
}
// 2. 具体饮料类 - 咖啡
class Coffee implements Beverage {
@Override
public double cost() {
return 5.0; // 咖啡的基础价格
}
@Override
public String description() {
return "Coffee";
}
}
// 3. 具体饮料类 - 茶
class Tea implements Beverage {
@Override
public double cost() {
return 3.0; // 茶的基础价格
}
@Override
public String description() {
return "Tea";
}
}
// 4. 装饰器类 - 加牛奶
class MilkDecorator implements Beverage {
private Beverage beverage; // 被装饰的饮料
public MilkDecorator(Beverage beverage) {
this.beverage = beverage;
}
@Override
public double cost() {
return beverage.cost() + 1.5; // 加牛奶的额外费用
}
@Override
public String description() {
return beverage.description() + " with Milk"; // 说明饮料添加了牛奶
}
}
// 5. 装饰器类 - 加糖浆
class SyrupDecorator implements Beverage {
private Beverage beverage; // 被装饰的饮料
public SyrupDecorator(Beverage beverage) {
this.beverage = beverage;
}
@Override
public double cost() {
return beverage.cost() + 2.0; // 加糖浆的额外费用
}
@Override
public String description() {
return beverage.description() + " with Syrup"; // 说明饮料添加了糖浆
}
}
// 6. 客户端使用
public class Main {
public static void main(String[] args) {
// 创建一杯基础咖啡
Beverage myCoffee = new Coffee();
System.out.println(myCoffee.description() + " costs " + myCoffee.cost());
// 给咖啡加牛奶
myCoffee = new MilkDecorator(myCoffee);
System.out.println(myCoffee.description() + " costs " + myCoffee.cost());
// 给咖啡加糖浆
myCoffee = new SyrupDecorator(myCoffee);
System.out.println(myCoffee.description() + " costs " + myCoffee.cost());
// 创建一杯茶
Beverage myTea = new Tea();
System.out.println(myTea.description() + " costs " + myTea.cost());
// 给茶加糖浆
myTea = new SyrupDecorator(myTea);
System.out.println(myTea.description() + " costs " + myTea.cost());
}
}
解析:
Beverage接口:定义了所有饮料的公共行为,包括计算价格(
cost()
)和获取描述(description()
)。Coffee和Tea类:具体的饮料类,分别实现了
cost()
和description()
方法,表示基础的咖啡和茶。MilkDecorator和SyrupDecorator类:装饰器类,分别为饮料添加了牛奶和糖浆。这些类通过包装原有的饮料对象,并在
cost()
和description()
方法中增加额外的功能。客户端代码:创建了一杯咖啡和茶,然后通过装饰器动态为这些饮料加上牛奶和糖浆。每次装饰后,饮料的价格和描述都发生了变化。
通过装饰者模式,饮料的原始类(如Coffee
和Tea
)无需做任何改变,你可以随时为它们增加新的功能(如牛奶、糖浆等)。这种方式非常灵活,不会影响原有类的设计,也使得功能的扩展变得非常方便。
实际应用场景:
UI组件:在GUI应用中,可能需要为组件(如按钮、文本框等)添加各种行为和样式。例如,你可以为按钮添加边框、阴影效果、动态效果等,而不需要修改按钮本身的实现。装饰者模式可以方便地为这些组件增加新功能。
流处理:Java的
InputStream
和OutputStream
就采用了装饰者模式。例如,BufferedInputStream
和DataInputStream
都可以装饰原始的InputStream
,为其添加缓冲和数据解析功能。这让流操作的扩展变得非常灵活。日志记录:在日志系统中,可能希望在不同的日志条目上添加不同的功能(例如,记录日志到文件、数据库、控制台等)。装饰者模式可以让你方便地为日志添加新的输出方式,而无需修改日志的核心逻辑。
总结:
装饰者模式提供了一种非常灵活的方式来“加装”功能,而无需修改原对象的代码。它让你可以动态地增强对象的行为,通过组合多个装饰器,可以创建功能丰富的对象。这种模式非常适合在不破坏原有结构的前提下扩展对象的功能。