装饰模式(Decorator Pattern)是一个结构型设计模式,它允许你通过将现有对象嵌入到封装器对象中来向对象添加行为。这种做法提供了一种灵活的替代方案,以替代继承来扩展类的功能。
简单易懂的生活例子:咖啡店
背景
想象你在一家咖啡店里,每次顾客点咖啡时,他们可以选择在基础咖啡上添加各种装饰,比如牛奶、糖、奶泡等等。每个装饰都可以独立地添加,不需要为每种组合创建一个新的咖啡子类。
实现装饰模式
基础构件
首先,我们需要一个表示咖啡的基础类
#include <iostream>
#include <string>
// 基础咖啡类
class Coffee {
public:
virtual std::string getDescription() const {
return "Basic Coffee";
}
virtual double cost() const {
return 5.0; // 假设基础咖啡价格为 5 元
}
virtual ~Coffee() = default; // 使用虚析构函数
};
装饰器基类
然后,我们创建一个装饰器基类,继承自 Coffee
。装饰器类也必须是一个 Coffee
,因此它可以替代 Coffee
对象使用。
class CoffeeDecorator : public Coffee {
protected:
Coffee* coffee; // 被装饰的实际对象
public:
CoffeeDecorator(Coffee* coffee) : coffee(coffee) {}
// 所有装饰器都需要重写这两个方法并添加自己的行为
std::string getDescription() const override {
return coffee->getDescription();
}
double cost() const override {
return coffee->cost();
}
};
具体的装饰器
现在让我们创建一些具体的装饰器,比如加牛奶和加糖
// 牛奶装饰器
class MilkDecorator : public CoffeeDecorator {
public:
MilkDecorator(Coffee* coffee) : CoffeeDecorator(coffee) {}
std::string getDescription() const override {
return coffee->getDescription() + ", Milk";
}
double cost() const override {
return coffee->cost() + 1.0; // 牛奶加 1 元
}
};
// 糖装饰器
class SugarDecorator : public CoffeeDecorator {
public:
SugarDecorator(Coffee* coffee) : CoffeeDecorator(coffee) {}
std::string getDescription() const override {
return coffee->getDescription() + ", Sugar";
}
double cost() const override {
return coffee->cost() + 0.5; // 糖加 0.5 元
}
};
示例使用
现在我们可以组合使用这些装饰器来动态地为咖啡对象增加功能。
int main() {
// 购买一杯基础咖啡
Coffee* basicCoffee = new Coffee();
std::cout << basicCoffee->getDescription() << ": " << basicCoffee->cost() << " 元" << std::endl;
// 购买一杯加了牛奶的咖啡
Coffee* milkCoffee = new MilkDecorator(basicCoffee);
std::cout << milkCoffee->getDescription() << ": " << milkCoffee->cost() << " 元" << std::endl;
// 购买一杯加了牛奶和糖的咖啡
Coffee* milkAndSugarCoffee = new SugarDecorator(milkCoffee);
std::cout << milkAndSugarCoffee->getDescription() << ": " << milkAndSugarCoffee->cost() << " 元" << std::endl;
// 清理内存
delete milkAndSugarCoffee;
delete milkCoffee;
delete basicCoffee;
return 0;
}
完整代码使用
#include <iostream>
#include <string>
// 基础咖啡类
class Coffee {
public:
virtual std::string getDescription() const {
return "Basic Coffee";
}
virtual double cost() const {
return 5.0; // 假设基础咖啡价格为 5 元
}
virtual ~Coffee() = default; // 使用虚析构函数
};
// 装饰器基类
class CoffeeDecorator : public Coffee {
protected:
Coffee* coffee; // 被装饰的实际对象
public:
CoffeeDecorator(Coffee* coffee) : coffee(coffee) {}
// 所有装饰器都需要重写这两个方法并添加自己的行为
std::string getDescription() const override {
return coffee->getDescription();
}
double cost() const override {
return coffee->cost();
}
};
// 牛奶装饰器
class MilkDecorator : public CoffeeDecorator {
public:
MilkDecorator(Coffee* coffee) : CoffeeDecorator(coffee) {}
std::string getDescription() const override {
return coffee->getDescription() + ", Milk";
}
double cost() const override {
return coffee->cost() + 1.0; // 牛奶加 1 元
}
};
// 糖装饰器
class SugarDecorator : public CoffeeDecorator {
public:
SugarDecorator(Coffee* coffee) : CoffeeDecorator(coffee) {}
std::string getDescription() const override {
return coffee->getDescription() + ", Sugar";
}
double cost() const override {
return coffee->cost() + 0.5; // 糖加 0.5 元
}
};
int main() {
// 购买一杯基础咖啡
Coffee* basicCoffee = new Coffee();
std::cout << basicCoffee->getDescription() << ": " << basicCoffee->cost() << " 元" << std::endl;
// 购买一杯加了牛奶的咖啡
Coffee* milkCoffee = new MilkDecorator(basicCoffee);
std::cout << milkCoffee->getDescription() << ": " << milkCoffee->cost() << " 元" << std::endl;
// 购买一杯加了牛奶和糖的咖啡
Coffee* milkAndSugarCoffee = new SugarDecorator(milkCoffee);
std::cout << milkAndSugarCoffee->getDescription() << ": " << milkAndSugarCoffee->cost() << " 元" << std::endl;
// 清理内存
delete milkAndSugarCoffee;
delete milkCoffee;
delete basicCoffee;
return 0;
}
总结
装饰模式的关键在于我们可以动态地组合装饰,不必创建大量的具体子类。通过这种方式,我们得到了更大的灵活性和可扩展性:
- 可以在运行时决定对象的组合。
- 每个装饰器只需要关心自己的附加行为,简化了类的设计。
- 更容易添加新的装饰,增加了系统的扩展性。
这个咖啡店的例子直观地展示了装饰模式在增加对象功能时避免子类爆炸的一种实用方法。