装饰器模式 (Decorator)
装饰器模式 是一种结构型设计模式,它允许动态地将责任附加到对象上,既可以在运行时给一个对象添加功能,又不会影响其他对象的功能。
意图
- 动态地扩展对象的功能。
- 避免创建过多的子类,通过装饰器来"包装"对象,添加新功能。
- 保持类的单一职责和开放封闭原则。
使用场景
- 系统需要动态地添加功能给对象 :
- 如UI框架中的组件装饰,能动态增加功能(如窗口的滚动条、边框等)。
- 不希望通过继承来扩展对象功能 :
- 继承可能会导致类的数量激增,装饰器模式能够解决这个问题。
- 功能扩展需要灵活控制 :
- 可以在不同的对象上应用不同的装饰,不会互相影响。
参与者角色
- 组件接口 (Component)
- 定义基本对象行为的接口,所有的装饰器和具体对象都需要实现此接口。
- 具体组件 (ConcreteComponent)
- 具体实现基本行为的对象。
- 装饰器 (Decorator)
- 持有组件的引用,并实现组件接口,增强功能。
- 具体装饰器 (ConcreteDecorator)
- 通过调用被装饰对象的方法来添加额外功能。
示例代码
以下代码展示了如何使用装饰器模式扩展一个简单的咖啡类。
cpp
#include <iostream>
#include <string>
// 组件接口
class Beverage {
public:
virtual ~Beverage() = default;
virtual std::string getDescription() const = 0;
virtual double cost() const = 0;
};
// 具体组件:咖啡
class Coffee : public Beverage {
public:
std::string getDescription() const override {
return "Coffee";
}
double cost() const override {
return 5.0;
}
};
// 装饰器基类
class BeverageDecorator : public Beverage {
protected:
Beverage* beverage;
public:
BeverageDecorator(Beverage* b) : beverage(b) {}
virtual ~BeverageDecorator() = default;
};
// 具体装饰器:加入牛奶
class MilkDecorator : public BeverageDecorator {
public:
MilkDecorator(Beverage* b) : BeverageDecorator(b) {}
std::string getDescription() const override {
return beverage->getDescription() + ", Milk";
}
double cost() const override {
return beverage->cost() + 1.0;
}
};
// 具体装饰器:加入糖
class SugarDecorator : public BeverageDecorator {
public:
SugarDecorator(Beverage* b) : BeverageDecorator(b) {}
std::string getDescription() const override {
return beverage->getDescription() + ", Sugar";
}
double cost() const override {
return beverage->cost() + 0.5;
}
};
// 客户端代码
int main() {
Beverage* coffee = new Coffee(); // 创建基本的咖啡
std::cout << coffee->getDescription() << " Cost: $" << coffee->cost() << std::endl;
// 加入牛奶的装饰
coffee = new MilkDecorator(coffee);
std::cout << coffee->getDescription() << " Cost: $" << coffee->cost() << std::endl;
// 再加入糖的装饰
coffee = new SugarDecorator(coffee);
std::cout << coffee->getDescription() << " Cost: $" << coffee->cost() << std::endl;
// 清理内存
delete coffee;
return 0;
}
代码解析
1. 组件接口
Beverage
类是所有具体组件和装饰器的公共接口,定义了所有具体对象的行为:
cpp
class Beverage {
public:
virtual ~Beverage() = default;
virtual std::string getDescription() const = 0;
virtual double cost() const = 0;
};
2. 具体组件
Coffee
是具体的组件,提供了描述和计算费用的功能:
cpp
class Coffee : public Beverage {
public:
std::string getDescription() const override {
return "Coffee";
}
double cost() const override {
return 5.0;
}
};
3. 装饰器基类
BeverageDecorator
继承了Beverage
,并持有一个Beverage
对象,能够动态地扩展其功能:
cpp
class BeverageDecorator : public Beverage {
protected:
Beverage* beverage;
public:
BeverageDecorator(Beverage* b) : beverage(b) {}
virtual ~BeverageDecorator() = default;
};
4. 具体装饰器
MilkDecorator
和SugarDecorator
是具体的装饰器类,分别增加了Milk
和Sugar
的功能:
cpp
class MilkDecorator : public BeverageDecorator {
public:
MilkDecorator(Beverage* b) : BeverageDecorator(b) {}
std::string getDescription() const override {
return beverage->getDescription() + ", Milk";
}
double cost() const override {
return beverage->cost() + 1.0;
}
};
cpp
class SugarDecorator : public BeverageDecorator {
public:
SugarDecorator(Beverage* b) : BeverageDecorator(b) {}
std::string getDescription() const override {
return beverage->getDescription() + ", Sugar";
}
double cost() const override {
return beverage->cost() + 0.5;
}
};
5. 客户端
- 客户端可以按需为原始对象添加装饰,每次调用
getDescription
和cost
时,都能看到新添加的功能:
cpp
int main() {
Beverage* coffee = new Coffee();
std::cout << coffee->getDescription() << " Cost: $" << coffee->cost() << std::endl;
coffee = new MilkDecorator(coffee);
std::cout << coffee->getDescription() << " Cost: $" << coffee->cost() << std::endl;
coffee = new SugarDecorator(coffee);
std::cout << coffee->getDescription() << " Cost: $" << coffee->cost() << std::endl;
delete coffee;
return 0;
}
优缺点
优点
- 动态扩展功能 :
- 可以在运行时为对象添加新功能。
- 避免子类数量暴增 :
- 通过装饰器而不是继承来扩展功能,避免了类的膨胀。
- 高灵活性 :
- 可以灵活地为不同的对象选择不同的装饰器进行组合。
缺点
- 增加系统复杂性 :
- 过多的装饰器可能使系统变得复杂,难以理解。
- 装饰器的顺序问题 :
- 装饰器的顺序会影响最终对象的行为。
适用场景
- 动态地给一个对象添加功能,例如为一个对象添加不同的用户界面样式、不同的服务功能。
- 避免使用继承,需要在运行时动态地扩展功能。
总结
装饰器模式是一种非常灵活的模式,能够动态地给对象添加新功能,避免了继承带来的问题。通过组合装饰器类,能够灵活地为对象组合不同的功能,在很多场景中非常有用。