想给你的对象增添新功能?不用修改原对象,装饰者帮你轻松“加装”功能,提升你的对象能力。

有时候,你的对象已经非常完美了,但你又希望在不改变它的情况下,增加一些新的功能。这时,你就需要一个“魔法工具”来让这个对象变得更加多才多艺。装饰者模式正是这种“魔法工具”,它允许你在运行时动态地给对象“加装”额外的功能,而不需要直接修改原对象的代码。就像是给汽车加装新的配件,原本的汽车性能并没有改变,但你可以加装导航、音响、座椅加热等,让它的功能变得更强大。

装饰者模式的精髓就在于:它提供了一种灵活的方式来扩展对象的行为,而不需要创建复杂的子类,也避免了直接修改原有代码带来的风险。它通过将功能包装到现有对象中,让你可以按需添加功能,而且装饰者的组合也十分灵活,可以创建出非常丰富的功能组合。

趣味解读:给你的小汽车加装功能!

想象一下,你买了一辆漂亮的小车,车子本身非常简洁、优雅。可是你发现自己有些新需求:你希望有座椅加热、蓝牙音响、天窗等功能,但你不想更换整辆车。于是你去汽车改装店,选择一个个配件,然后“加装”到你的车上。这些配件本身并不会改变小车的外观或基础功能,它们只会增添额外的特性,让小车变得更强大。

这就是装饰者模式的操作方式:你不需要拆解原有对象,只需要在其外层添加一些装饰类来“增强”功能,最终得到一个功能更全的小车(或者对象)。

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()方法中增加额外的功能。

  • 客户端代码:创建了一杯咖啡和茶,然后通过装饰器动态为这些饮料加上牛奶和糖浆。每次装饰后,饮料的价格和描述都发生了变化。

通过装饰者模式,饮料的原始类(如CoffeeTea)无需做任何改变,你可以随时为它们增加新的功能(如牛奶、糖浆等)。这种方式非常灵活,不会影响原有类的设计,也使得功能的扩展变得非常方便。

实际应用场景:

  1. UI组件:在GUI应用中,可能需要为组件(如按钮、文本框等)添加各种行为和样式。例如,你可以为按钮添加边框、阴影效果、动态效果等,而不需要修改按钮本身的实现。装饰者模式可以方便地为这些组件增加新功能。

  2. 流处理:Java的InputStreamOutputStream就采用了装饰者模式。例如,BufferedInputStreamDataInputStream都可以装饰原始的InputStream,为其添加缓冲和数据解析功能。这让流操作的扩展变得非常灵活。

  3. 日志记录:在日志系统中,可能希望在不同的日志条目上添加不同的功能(例如,记录日志到文件、数据库、控制台等)。装饰者模式可以让你方便地为日志添加新的输出方式,而无需修改日志的核心逻辑。

总结:

装饰者模式提供了一种非常灵活的方式来“加装”功能,而无需修改原对象的代码。它让你可以动态地增强对象的行为,通过组合多个装饰器,可以创建功能丰富的对象。这种模式非常适合在不破坏原有结构的前提下扩展对象的功能。