• 【C++设计模式】详解装饰模式


    2023年8月31日,周四上午

    这是我目前碰到的最难的设计模式.....

    非常难以理解而且比较灵活多半,学得贼难受,写得贼费劲.....

    2023年8月31日,周四晚上19:48

    终于写完了,花了一天的时间来学习装饰模式和写这篇博客。

    虽然基本上把我今天的收获都写下来了,

    但感觉写得还是不够好,有很多东西没有表达出来、表达清楚,

    以后有空再更新吧....


    目录


    概述

    装饰模式的含义

    装饰模式是一种结构型设计模式,它允许向一个现有的对象添加新的功能,同时又不改变其结构。

    使用装饰模式的好处

    • 可以动态地给对象添加责任,扩展对象功能。
    • 相比继承,装饰模式使用对象组合而不是继承来扩展功能。更加灵活。
    • 装饰模式支持开闭原则,扩展对象不修改其代码。

    为什么需要装饰模式

    1. 动态扩展功能:装饰模式允许在运行时动态地给对象添加新的功能,而不需要修改原始对象的代码。这样可以避免类的继承层次的爆炸性增长,同时也更加灵活和可扩展。
    2. 单一职责原则:装饰模式通过将功能分散到多个装饰类中,每个装饰类只关注特定的功能,遵循了单一职责原则,使得代码更加清晰、可维护。
    3. 开放-封闭原则:装饰模式支持对现有代码的扩展,而不需要修改已有的代码,符合开放-封闭原则。这样可以避免因为修改现有代码而引入潜在的风险和错误。

    什么时候使用装饰模式

    1. 需要在不改变现有对象结构的情况下,给对象增加新的功能或责任。
    2. 需要动态地给对象添加功能,并且这些功能可以组合和排列。
    3. 需要扩展一个类的功能,但是使用继承会导致类的继承层次过于庞大或不可行。
    4. 需要在运行时动态地给对象添加功能,而不是在编译时静态地确定功能。

    情景

    假设我有一个类Xxx,需要给它添加加法功能和减法功能,

    但是由于这个类已经很复杂了、而且继承的层次已经很深了,

    我不想改动里面的代码,也不想再多弄一个子类,那么怎么添加这两个功能呢?

    1. class Xxx:public Ppp{
    2. public:
    3. void func1();
    4. void func2();
    5. void func3();
    6. void func4();
    7. void func5();
    8. void func6();
    9. void func7();
    10. };

    在这种情况下,可以使用装饰模式来添加这两个功能,

    因为这样既不用改动类内部代码,也不用再多弄一个子类。

    1. // 原始类
    2. class Xxx :public Ppp{
    3. public:
    4. void func1() { /* 原始功能1的实现 */ }
    5. void func2() { /* 原始功能2的实现 */ }
    6. // ... 其他原始功能的实现
    7. void func7() { /* 原始功能7的实现 */ }
    8. };
    9. // 装饰类 - 加法功能
    10. class AddDecorator : public Xxx {
    11. private:
    12. Xxx* component;
    13. public:
    14. AddDecorator(Xxx* component) : component(component) {}
    15. void func1() override {
    16. component->func1();
    17. // 加法功能的实现
    18. }
    19. };
    20. // 装饰类 - 减法功能
    21. class SubtractDecorator : public Xxx {
    22. private:
    23. Xxx* component;
    24. public:
    25. SubtractDecorator(Xxx* component) : component(component) {}
    26. void func2() override {
    27. component->func2();
    28. // 减法功能的实现
    29. }
    30. };
    31. int main() {
    32. Xxx* xxx = new Xxx();
    33. // 使用加法功能的装饰类
    34. Xxx* xxxWithAddition = new AddDecorator(xxx);
    35. xxxWithAddition->func1();
    36. // 使用减法功能的装饰类
    37. Xxx* xxxWithSubtraction = new SubtractDecorator(xxx);
    38. xxxWithSubtraction->func2();
    39. delete xxxWithSubtraction;
    40. delete xxxWithAddition;
    41. delete xxx;
    42. return 0;
    43. }

    注意,这并不是一个完整的装饰模式,但有助于理解装饰模式的作用。

    标准的装饰模式

    装饰模式的主要特征

    • 有一个抽象构件类,定义对象的接口。
    • 具体构件类实现抽象构件类。
    • 有一个装饰类继承抽象构件类,包含抽象构件类的对象引用。
    • 装饰类可以调用父类接口实现扩展功能。

    标准装饰模式程序示例

    1. #include
    2. // 抽象构件类
    3. class Component {
    4. public:
    5. virtual void func() = 0;
    6. };
    7. // 具体构件类
    8. class ConcreteComponent : public Component {
    9. public:
    10. void func() override {
    11. std::cout << "具体构件的操作" << std::endl;
    12. }
    13. };
    14. // 装饰类
    15. class Decorator : public Component {
    16. protected:
    17. Component* component;
    18. public:
    19. Decorator(Component* component) : component(component) {}
    20. void func() override {
    21. if (component != nullptr) {
    22. component->func();
    23. }
    24. }
    25. };
    26. // 具体装饰类A
    27. class ConcreteDecoratorA : public Decorator {
    28. public:
    29. ConcreteDecoratorA(Component* component) : Decorator(component) {}
    30. void addedBehavior() {
    31. std::cout << "具体装饰类的附加行为A" << std::endl;
    32. }
    33. void func() override {
    34. Decorator::func();
    35. addedBehavior();
    36. }
    37. };
    38. // 具体装饰类B
    39. class ConcreteDecoratorB : public Decorator {
    40. public:
    41. ConcreteDecoratorB(Component* component) : Decorator(component) {}
    42. void addedBehavior() {
    43. std::cout << "具体装饰类的附加行为B" << std::endl;
    44. }
    45. void func() override {
    46. Decorator::func();
    47. addedBehavior();
    48. }
    49. };
    50. int main() {
    51. Component* component = new ConcreteComponent();
    52. //用decoratedComponentA修饰component
    53. Component* decoratedComponentA = new ConcreteDecoratorA(component);
    54. //用decoratedComponentB修饰被decoratedComponentA修饰过的component
    55. Component* decoratedComponentB = new ConcreteDecoratorB(decoratedComponentA);
    56. decoratedComponentB->func();
    57. //要记得释放申请的堆内存
    58. delete decoratedComponentA;
    59. delete decoratedComponentB;
    60. delete component;
    61. return 0;
    62. }

    我对标准装饰模式的思考

    装饰模式是怎么实现的不改动类原来的代码和结构就增加新的功能的?
    装饰器是如何装饰具体构建类的?

    他们有同一个抽象父类
    这意味这什么?它们都会有抽象构件类的纯虚函数

    装饰器需要引入抽象构件类
    能引入抽象构件类,意味传入具体构件类对象后,只能调用其中抽象构件类有的方法
    装饰器会重写父类的方法,并在重写的方法里面调用具体构件类的方法

    具体装饰器有自己独特的成员和方法
    具体装饰器会把具体构件类装入
    具体装饰器会重写抽象装饰器的方法,并在重写的方法里面调用父类的方法和自己独特的方法
    不管怎样,具体装饰类必须引入抽象构件类并传入具体构件类对象

    装饰模式的核心在于重写?
    重写的时候加入额外的方法

    具体构件类需要实现抽象构件类的纯虚函数,否则无法被创建

    装饰器的作用是什么?
    引入具体构件类
    在重写方法中调用具体构件类的方法

    抽象构件类的作用是什么?

    具体构件类的作用是什么?
    定义最基础的功能

    具体装饰器的作用是什么?
    写额外的功能,然后通过重写方法加入额外的功能

    使用标准装饰模式来写的示例程序

    1. #include
    2. class Product{
    3. public:
    4. virtual void buy()=0;
    5. };
    6. class Computer:public Product{
    7. public:
    8. void buy ()override{
    9. std::cout<<"买了一台电脑"<
    10. }
    11. };
    12. class Decorate:public Product{
    13. private:
    14. Product *product;
    15. public:
    16. Decorate(Product *product):product(product){};
    17. void buy() override{
    18. if(product!=nullptr){
    19. product->buy();
    20. }
    21. }
    22. };
    23. class Mouse:public Decorate{
    24. public:
    25. Mouse(Product *product):Decorate(product){};
    26. void buy() override{
    27. Decorate::buy();
    28. std::cout<<"又买了鼠标"<
    29. }
    30. };
    31. class KeyBoard:public Decorate{
    32. public:
    33. KeyBoard(Product *product):Decorate(product){};
    34. void buy() override{
    35. Decorate::buy();
    36. std::cout<<"又买了键盘"<
    37. }
    38. };
    39. int main(){
    40. Product *computer=new Computer();
    41. Mouse *mouse=new Mouse(computer);
    42. KeyBoard *keyBoard=new KeyBoard(mouse);
    43. keyBoard->buy();
    44. }

  • 相关阅读:
    Leetcode:【485. 最大连续 1 的个数】
    Nginx访问控制与虚拟主机
    Redis源码学习(31),字典学习,dict.c(一)
    【userfaultfd+msg_msg+pipe_buffer】CISCN2022-cactus
    React - Ant Design3.x版本安装使用,并按需引入和自定义主题
    Spark SQL简介
    如何评价最近爆红的FastAPI?
    定制SQLmap和WAF绕过
    【图论】图文详解Tarjan算法有向图缩点
    宁德时代打响增长保卫战
  • 原文地址:https://blog.csdn.net/m0_61629312/article/details/132600100