结构型模式4.装饰器模式

发布于:2025-02-10 ⋅ 阅读:(55) ⋅ 点赞:(0)

结构型模式

  1. 适配器模式(Adapter Pattern)
  2. 桥接模式(Bridge Pattern)
  3. 组合模式(Composite Pattern)
  4. 装饰器模式(Decorator Pattern)
  5. 外观模式(Facade Pattern)
  6. 享元模式(Flyweight Pattern)
  7. 代理模式(Proxy Pattern)

装饰器模式(Decorator Pattern)是一种结构型设计模式,旨在动态地给一个对象添加额外的功能,而不需要修改其结构。装饰器模式通过使用组合(而非继承)来扩展对象的功能,允许你在运行时对对象进行扩展。

装饰器模式的核心思想:

  • 不改变对象的原有功能,而是在原有功能的基础上增加新的功能
  • 通过将装饰功能封装在装饰器类中,使得功能的增加和修改更加灵活。
  • 装饰器模式通常与继承相对,继承会通过扩展类来增加功能,而装饰器模式是通过组合不同的装饰器对象来增强功能。

主要角色:

  1. Component(组件):定义了一个接口,通常是一个抽象类或者接口,规定了被装饰的对象和装饰器的共同接口。
  2. ConcreteComponent(具体组件):实现了Component接口的基本功能,是被装饰的目标对象。
  3. Decorator(装饰器):是一个抽象类,持有一个Component对象,并在其功能上进行扩展。它继承了Component接口,并将所有方法委托给实际的Component对象。
  4. ConcreteDecorator(具体装饰器):继承自Decorator,负责具体的功能扩展(例如添加新功能或修改现有功能)。

装饰器模式的示例:咖啡店

假设我们要设计一个咖啡的销售系统,其中有不同种类的咖啡(例如:单纯的黑咖啡、加了牛奶的咖啡、加了糖的咖啡等),每种咖啡在基础上都有不同的装饰。我们可以使用装饰器模式来动态地为咖啡添加不同的配料。

// 组件接口,所有的咖啡都实现这个接口
public interface Coffee {
    double cost(); // 计算价格
    String ingredients(); // 获取配料信息
}

// 具体组件,代表普通的黑咖啡
public class BlackCoffee implements Coffee {
    @Override
    public double cost() {
        return 5.0; // 黑咖啡的基础价格
    }

    @Override
    public String ingredients() {
        return "Black Coffee"; // 黑咖啡的配料
    }
}

// 装饰器,持有一个Coffee对象,并且可以增强其功能
public abstract class CoffeeDecorator implements Coffee {
    protected Coffee coffee; // 持有一个咖啡对象

    public CoffeeDecorator(Coffee coffee) {
        this.coffee = coffee;
    }

    @Override
    public double cost() {
        return coffee.cost(); // 委托给被装饰的咖啡对象
    }

    @Override
    public String ingredients() {
        return coffee.ingredients(); // 委托给被装饰的咖啡对象
    }
}


// 装饰器1:添加牛奶
public class MilkDecorator extends CoffeeDecorator {
    public MilkDecorator(Coffee coffee) {
        super(coffee);
    }

    @Override
    public double cost() {
        return coffee.cost() + 2.0; // 加牛奶的价格
    }

    @Override
    public String ingredients() {
        return coffee.ingredients() + ", Milk"; // 牛奶的配料
    }
}

// 装饰器2:添加糖
public class SugarDecorator extends CoffeeDecorator {
    public SugarDecorator(Coffee coffee) {
        super(coffee);
    }

    @Override
    public double cost() {
        return coffee.cost() + 1.0; // 加糖的价格
    }

    @Override
    public String ingredients() {
        return coffee.ingredients() + ", Sugar"; // 糖的配料
    }
}

public class Client {
    public static void main(String[] args) {
        // 创建一个黑咖啡
        Coffee coffee = new BlackCoffee();
        System.out.println("Cost: " + coffee.cost()); // 输出:5.0
        System.out.println("Ingredients: " + coffee.ingredients()); // 输出:Black Coffee

        // 给黑咖啡添加牛奶
        coffee = new MilkDecorator(coffee);
        System.out.println("Cost: " + coffee.cost()); // 输出:7.0
        System.out.println("Ingredients: " + coffee.ingredients()); // 输出:Black Coffee, Milk

        // 给已经添加牛奶的咖啡再添加糖
        coffee = new SugarDecorator(coffee);
        System.out.println("Cost: " + coffee.cost()); // 输出:8.0
        System.out.println("Ingredients: " + coffee.ingredients()); // 输出:Black Coffee, Milk, Sugar
    }
}

解释:

  • Component(Coffee):定义了咖啡的基础功能,所有的咖啡类都实现了这个接口,提供 cost() 和 ingredients() 方法。
  • ConcreteComponent(BlackCoffee):表示一种具体的咖啡(黑咖啡),实现了 Coffee 接口。
  • Decorator(CoffeeDecorator):是一个抽象类,持有一个 Coffee 对象,允许在此基础上添加新功能。
  • ConcreteDecorator(MilkDecorator 和 SugarDecorator):具体装饰器,分别为咖啡添加牛奶和糖。它们在原有功能的基础上进行扩展。

装饰器模式的优缺点:

优点:
  1. 增强灵活性:装饰器模式允许动态地向对象添加功能,而不需要修改原有对象的代码。这种方式比继承更灵活。
  2. 减少子类的创建:通过装饰器模式,我们不需要为每一种不同的组合创建一个子类,而是通过不同的装饰器组合出所需的功能。
  3. 符合开闭原则:装饰器模式遵循开闭原则,即可以对功能进行扩展而不需要修改原有代码。
缺点:
  1. 装饰器过多时,系统会变得很复杂:如果装饰器链太长,可能会造成代码的复杂度增加,使得调试和理解变得更加困难。
  2. 可能导致过多的小对象:每个装饰器本身就是一个对象,因此在复杂的系统中,可能会创建大量的小对象,增加内存开销。

总结:

装饰器模式通过将功能与对象的实现分离,使得我们能够在运行时动态地增加对象的功能,而不需要修改对象本身。它能够灵活地扩展对象功能,减少了子类数量,提升了系统的灵活性和可维护性。