设计模式-装饰模式

装饰模式(Decorator Pattern) 是结构型设计模式之一,它允许动态地给一个对象添加一些额外的职责,而无需改变其结构。装饰模式通过创建一个装饰类,使用继承的方式来扩展对象的功能,从而避免了大量的子类化问题。

装饰模式的关键要点:

  1. 组件接口(Component):定义一个接口,表示被装饰的对象和装饰器的共同接口。
  2. 具体组件类(ConcreteComponent):实现组件接口,表示被装饰的具体对象。
  3. 装饰器类(Decorator):持有一个 Component 对象的引用,并通过委托给该对象来扩展其功能。
  4. 具体装饰器类(ConcreteDecorator):继承自装饰器类,并添加额外的功能。

适用场景:

  • 需要扩展对象的功能,但又不想通过继承的方式增加大量的子类。
  • 动态地给一个对象添加职责,而不影响其他对象。
  • 需要为多个对象添加相同功能时,避免在每个类中都实现重复代码。

具体步骤:

  1. 定义一个组件接口,它可以是一个抽象类或接口。
  2. 创建一个具体组件类,实现该接口或抽象类,代表基本功能。
  3. 创建装饰器类,持有一个组件对象并扩展其功能。
  4. 创建多个具体装饰器类,给对象添加具体的附加功能。

代码示例

假设我们有一个饮料类,饮料可以有不同的配料(如牛奶、糖、巧克力等)。我们希望通过装饰模式来动态添加这些配料,而无需修改原始的饮料类。

1. 定义组件接口

// 组件接口
public interface Beverage {
    double cost();  // 计算饮料的费用
    String description();  // 获取饮料的描述
}

2. 创建具体组件类

// 具体组件:基础饮料
public class Coffee implements Beverage {

    @Override
    public double cost() {
        return 5.0;  // 咖啡的基本价格
    }

    @Override
    public String description() {
        return "Coffee";  // 饮料的描述
    }
}

3. 创建装饰器类

// 装饰器类,持有一个 Beverage 对象
public abstract class BeverageDecorator implements Beverage {
    protected Beverage beverage;  // 持有一个 Beverage 对象的引用

    public BeverageDecorator(Beverage beverage) {
        this.beverage = beverage;
    }

    @Override
    public double cost() {
        return beverage.cost();  // 委托给 beverage 的 cost 方法
    }

    @Override
    public String description() {
        return beverage.description();  // 委托给 beverage 的 description 方法
    }
}

4. 创建具体装饰器类

// 具体装饰器1:添加牛奶
public class MilkDecorator extends BeverageDecorator {

    public MilkDecorator(Beverage beverage) {
        super(beverage);  // 构造器中传入 Beverage 对象
    }

    @Override
    public double cost() {
        return beverage.cost() + 1.5;  // 牛奶的附加费用
    }

    @Override
    public String description() {
        return beverage.description() + " with Milk";  // 饮料描述中加上“牛奶”
    }
}

// 具体装饰器2:添加糖
public class SugarDecorator extends BeverageDecorator {

    public SugarDecorator(Beverage beverage) {
        super(beverage);  // 构造器中传入 Beverage 对象
    }

    @Override
    public double cost() {
        return beverage.cost() + 0.5;  // 糖的附加费用
    }

    @Override
    public String description() {
        return beverage.description() + " with Sugar";  // 饮料描述中加上“糖”
    }
}

5. 客户端代码使用装饰模式

public class Main {
    public static void main(String[] args) {
        // 创建一杯咖啡
        Beverage beverage = new Coffee();

        // 给咖啡加牛奶
        beverage = new MilkDecorator(beverage);

        // 给咖啡加糖
        beverage = new SugarDecorator(beverage);

        // 打印饮料的描述和价格
        System.out.println("Description: " + beverage.description());  // Coffee with Milk with Sugar
        System.out.println("Cost: " + beverage.cost());  // 7.0
    }
}

输出:

Description: Coffee with Milk with Sugar
Cost: 7.0

解释:

  1. Beverage 接口 定义了所有饮料(包括装饰器和具体饮料类)的共同行为。它有两个方法:

    • cost() 用来计算饮料的费用。
    • description() 用来描述饮料。
  2. Coffee 类 实现了 Beverage 接口,表示基础饮料,计算基础费用并返回描述。

  3. BeverageDecorator 类 是一个抽象装饰器类,持有一个 Beverage 对象的引用,并通过委托的方式调用 Beverage 的方法。具体的装饰器类继承自这个类,并扩展其功能。

  4. MilkDecorator 和 SugarDecorator 类 是具体装饰器类,它们通过装饰原始饮料对象来增加功能(例如,添加牛奶和糖),并修改其 cost()description() 方法。

  5. 客户端代码 创建了一个 Coffee 对象,并使用装饰器动态添加了牛奶和糖,最后打印出饮料的描述和价格。

优点:

  • 增强对象的功能:装饰模式能够在不修改类的情况下增强对象的功能。
  • 避免类爆炸:通过组合多个装饰器来实现不同的功能,而不是通过继承创建大量的子类。
  • 可以动态改变对象的行为:装饰模式可以在运行时动态地给对象增加行为,而不需要修改对象的原始代码。

缺点:

  • 装饰器的数量过多时会变得复杂:如果装饰器过多,使用时会变得复杂,因为需要组合多个装饰器来完成任务。
  • 可能会导致过多的类:每增加一个新的装饰功能,就需要增加一个装饰器类,可能会导致类的数量急剧增加。

总结:

装饰模式提供了一种灵活的方式来为对象动态地添加新功能。它可以有效避免子类化带来的问题,支持对象行为的动态改变,并且在许多情况下优于继承。当你需要扩展对象的功能并且希望能动态地添加功能时,装饰模式是一个非常合适的选择。

posted @   庞某人  阅读(10)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 推荐几款开源且免费的 .NET MAUI 组件库
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· 【全网最全教程】使用最强DeepSeekR1+联网的火山引擎,没有生成长度限制,DeepSeek本体
点击右上角即可分享
微信分享提示