定义
装饰模式(Decorator Pattern),又称为包装模式,是一种结构型设计模式。它允许在不改变现有对象结构的情况下,动态地添加新的功能。通过将每个功能封装在单独的装饰器类中,并且这些装饰器类通过引用原始对象来实现功能的组合,从而提供了灵活性和可扩展性的优势。装饰模式避免了通过继承方式增加功能所带来的复杂性,使得功能的添加和组合更加高效和清晰。
为什么使用装饰模式?
-
灵活性和可扩展性
- 装饰模式允许按需动态地添加或移除对象的功能,而不会影响其他部分的代码。
- 通过组合不同的装饰器类,可以创建出多种不同的对象组合,以满足不同的需求,避免了静态继承所带来的类爆炸问题。
-
单一职责原则
- 每个装饰器类只关注于一个特定的功能或责任,这符合单一职责原则,使得代码结构更加清晰和易于维护。
-
透明性
- 装饰器类与原始对象实现相同的接口,因此对客户端来说是透明的。
- 客户端可以像使用原始对象一样使用装饰后的对象,无需关心对象内部的具体装饰结构。
装饰模式的实现步骤
-
抽象组件类
- 定义了被装饰对象的接口,它可能是一个抽象类或接口,包含了所有具体组件类和装饰器类都会实现的方法(抽象被装饰者的行为)。
-
具体组件类
- 实现抽象组件接口,表示原始对象的基本行为或功能(继承抽象组件类,被装饰者行为的具体实现)。
-
抽象装饰器类
- 扩展了抽象组件类,同时持有一个指向抽象组件对象的引用。
- 这个类可以选择性地添加一些额外的行为,但其主要作用是通过引用调用原始对象的方法(继承抽象组件类)。
-
具体装饰器类
- 扩展了抽象装饰器类,通过在调用父类方法前后添加新的行为来实现功能的扩展。
- 具体装饰器类可以是多个,可以互相组合,以形成复杂的装饰结构(继承抽象装饰器类)。
优缺点和适用场景
优点
-
灵活性
- 动态地为对象添加功能,避免了静态继承的限制。
-
可扩展性
- 通过组合不同的装饰器类,可以实现多种功能组合,符合开闭原则。
-
单一职责原则
- 每个装饰器类只关注于一个功能,使得代码结构清晰。
缺点
-
复杂性增加
- 可能会导致装饰器类的数量增加,增加了系统的复杂度和理解难度。
-
装饰顺序问题
- 如果装饰器的顺序不正确,可能会影响最终的功能实现。
适用场景
-
动态添加功能
- 当需要动态地为对象添加额外功能时,而又不希望生成大量子类时,可以使用装饰模式。
-
透明且灵活地扩展对象的功能
- 当需要透明且灵活地扩展对象的功能时,装饰模式尤为适用,例如在不影响其他对象的情况下动态添加功能。
咖啡店的例子
假设我们有一个咖啡店,卖基础的咖啡和各种附加调料(如牛奶、糖、巧克力等)。我们可以使用装饰模式来实现这种功能扩展。
#include <iostream> #include <memory> // 提供智能指针的定义和实现 // 抽象组件类:咖啡 class Coffee { public: virtual ~Coffee() {} // 获取咖啡描述的方法,纯虚函数 virtual std::string getDescription() const = 0; // 获取咖啡价格的方法,纯虚函数 virtual double cost() const = 0; }; // 具体组件类:基本咖啡 class BasicCoffee : public Coffee { public: // 实现获取描述的方法 std::string getDescription() const override { return "Basic Coffee"; } // 实现获取价格的方法 double cost() const override { return 5.0; // 基本咖啡的价格 } }; // 抽象装饰器类:咖啡装饰器 class CoffeeDecorator : public Coffee { protected: // 持有一个指向被装饰对象的指针 std::shared_ptr<Coffee> coffee; public: // 构造函数,接受一个被装饰对象的指针 CoffeeDecorator(std::shared_ptr<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(std::shared_ptr<Coffee> coffee) : CoffeeDecorator(coffee) {} // 实现获取描述的方法,添加牛奶的描述 std::string getDescription() const override { return coffee->getDescription() + ", Milk"; } // 实现获取价格的方法,添加牛奶的价格 double cost() const override { return coffee->cost() + 1.5; // 牛奶的价格 } }; // 具体装饰器类:糖装饰器 class SugarDecorator : public CoffeeDecorator { public: // 构造函数,接受一个被装饰对象的指针 SugarDecorator(std::shared_ptr<Coffee> coffee) : CoffeeDecorator(coffee) {} // 实现获取描述的方法,添加糖的描述 std::string getDescription() const override { return coffee->getDescription() + ", Sugar"; } // 实现获取价格的方法,添加糖的价格 double cost() const override { return coffee->cost() + 0.5; // 糖的价格 } }; int main() { // 创建一个基本咖啡对象 std::shared_ptr<Coffee> basicCoffee = std::make_shared<BasicCoffee>(); std::cout << "Description: " << basicCoffee->getDescription() << ", Cost: " << basicCoffee->cost() << " RMB" << std::endl; // 用牛奶装饰基本咖啡 std::shared_ptr<Coffee> coffeeWithMilk = std::make_shared<MilkDecorator>(basicCoffee); std::cout << "Description: " << coffeeWithMilk->getDescription() << ", Cost: " << coffeeWithMilk->cost() << " RMB" << std::endl; // 再用糖装饰已加牛奶的咖啡 std::shared_ptr<Coffee> coffeeWithMilkAndSugar = std::make_shared<SugarDecorator>(coffeeWithMilk); std::cout << "Description: " << coffeeWithMilkAndSugar->getDescription() << ", Cost: " << coffeeWithMilkAndSugar->cost() << " RMB" << std::endl; return 0; }
1.本站内容仅供参考,不作为任何法律依据。用户在使用本站内容时,应自行判断其真实性、准确性和完整性,并承担相应风险。
2.本站部分内容来源于互联网,仅用于交流学习研究知识,若侵犯了您的合法权益,请及时邮件或站内私信与本站联系,我们将尽快予以处理。
3.本文采用知识共享 署名4.0国际许可协议 [BY-NC-SA] 进行授权
4.根据《计算机软件保护条例》第十七条规定“为了学习和研究软件内含的设计思想和原理,通过安装、显示、传输或者存储软件等方式使用软件的,可以不经软件著作权人许可,不向其支付报酬。”您需知晓本站所有内容资源均来源于网络,仅供用户交流学习与研究使用,版权归属原版权方所有,版权争议与本站无关,用户本人下载后不能用作商业或非法用途,需在24个小时之内从您的电脑中彻底删除上述内容,否则后果均由用户承担责任;如果您访问和下载此文件,表示您同意只将此文件用于参考、学习而非其他用途,否则一切后果请您自行承担,如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。
5.本站是非经营性个人站点,所有软件信息均来自网络,所有资源仅供学习参考研究目的,并不贩卖软件,不存在任何商业目的及用途
暂无评论内容