0 结构型模式

让类和类进行组合,获得更大的结构,获得新功能的方式。

1 代理模式

Proxy 模式又被叫做代理模式,是结构型的设计模式之一,它可以 为其他对象提供一种代理以控制对这个对象的访问

所谓 代理,是指具有与代理元(被代理的对象)具有相同的接口的类,客户端必须 通过代理与被代理的目标类交互,而代理一般在交互的过程中,进行某些特别的处理。

1.1 模式中的角色和职责

subject(抽象主题角色):真实主题与代理主题的共同接口。

RealSubject(真实主题角色):定义了代理角色所代表的真实对象。

Proxy(代理主题角色):含有对真实主题角色的引用,代理角色通常在将客户端调用传递给真实主题对象之前或者之后执行某些操作,而不是单纯返回真实的对象。

1.2 案例

示例代码:

  1. #include <iostream>
  2. #include <string>
  3. using namespace std;
  4. // 物品
  5. class Item
  6. {
  7. public:
  8. Item(string kind,bool fake)
  9. {
  10. this->kind = kind;
  11. this->fake = fake;
  12. }
  13. // 物品类别
  14. string kind;
  15. // true:假货,false:真货
  16. bool fake;
  17. };
  18. // subject(抽象主题角色),抽象的购物方式,具有买的功能
  19. class Shopping
  20. {
  21. public:
  22. virtual void buy(Item& item) = 0;
  23. };
  24. // RealSubject(真实主题角色),去韩国购物
  25. class KoreaShopping : public Shopping
  26. {
  27. public:
  28. virtual void buy(Item& item)
  29. {
  30. cout << "去韩国购物,买了" << item.kind << endl;
  31. }
  32. };
  33. // RealSubject(真实主题角色),去美国购物
  34. class USAShopping : public Shopping
  35. {
  36. public:
  37. virtual void buy(Item& item)
  38. {
  39. cout << "去美国购物,买了" << item.kind << endl;
  40. }
  41. };
  42. // RealSubject(真实主题角色),去非洲购物
  43. class AfricaShopping : public Shopping
  44. {
  45. public:
  46. virtual void buy(Item& item)
  47. {
  48. cout << "去非洲购物,买了" << item.kind << endl;
  49. }
  50. };
  51. // Proxy(代理主题角色),海外代购 代理,实现了购物模式,而且还增加了辨别货物真伪和海关安检具体业务。
  52. class OverseasProxy : public Shopping
  53. {
  54. public:
  55. OverseasProxy(Shopping* shopping)
  56. {
  57. this->shopping = shopping;
  58. }
  59. ~OverseasProxy()
  60. {
  61. delete this->shopping;
  62. }
  63. bool distinguish(Item& item)
  64. {
  65. cout << "对物品[" << item.kind << "]辨别真伪." << endl;
  66. return item.fake;
  67. }
  68. void check(Item& item)
  69. {
  70. cout << "通过海关安检,带回国内" << endl;
  71. }
  72. virtual void buy(Item& item)
  73. {
  74. if(distinguish(item) == false)
  75. {
  76. this->shopping->buy(item);
  77. check(item);
  78. }
  79. else
  80. {
  81. cout << "发现伪货[" << item.kind << "],不能购买" << endl;
  82. }
  83. }
  84. private:
  85. Shopping* shopping;
  86. };
  87. int main(void)
  88. {
  89. Item item1("化妆品",false);
  90. Item item2("学位证",true);
  91. Item item3("空调",false);
  92. Shopping* shopping = NULL;
  93. OverseasProxy* proxy = NULL;
  94. // 1.去韩国买化妆品
  95. proxy = new OverseasProxy(new KoreaShopping);
  96. proxy->buy(item1);
  97. delete proxy;
  98. // 2.去美国买学位证
  99. proxy = new OverseasProxy(new USAShopping);
  100. proxy->buy(item2);
  101. delete proxy;
  102. // 3.去非洲买空调
  103. proxy = new OverseasProxy(new AfricaShopping);
  104. proxy->buy(item3);
  105. delete proxy;
  106. return 0;
  107. }

运行结果:

1.3 代理模式的种类

(1) 远程代理(Remote Proxy):为一个位于不同的地址空间的对象提供一个本地的代理对象,这个不同的地址空间可以是在同一台主机中,也可是在另一台主机中,远程代理又称为大使(Ambassador)

(2)虚拟代理(Virtual Proxy):如果需要创建一个资源消耗较大的对象,先创建一个消耗相对较小的对象来表示,真实对象只在需要时才会被真正创建。

(3)保护代理(Protect Proxy):控制对一个对象的访问,可以给不同的用户提供不同级别的使用权限。

(4)缓冲代理(Cache Proxy):为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以共享这些结果。

(5)智能引用代理(Smart Reference Proxy):当一个对象被引用时,提供一些额外的操作,例如将对象被调用的次数记录下来等

1.4 优缺点

优点:

(1)能够协调调用者和被调用者,在一定程度上降低了系统的耦合度。

(2)客户端可以针对抽象主题角色进行编程,增加和更换代理类无须修改源代码,符合开闭原则,系统具有较好的灵活性和可扩展性。

缺点:

(1)代理实现较为复杂

1.5 适用场景

为其他对象提供一种代理以控制对这个对象的访问。

2 装饰模式

装饰模式(Decorator Pattern):动态地给一个对象增加一些额外的职责就增加对象功能来说,装饰模式 比生成子类实现更为灵活

2.1 模式中的角色和职责

Component(抽象构件):它是 具体构件和抽象装饰类的共同父类,声明了在具体构件中实现的业务方法,它的引入可以使客户端以一致的方式处理未被装饰的对象以及装饰之后的对象,实现客户端的透明操作。

ConcreteComponent(具体构件):它是抽象构件类的子类,用于定义具体的构件对象,实现了在抽象构件中声明的方法,装饰器可以给它增加额外的职责(方法)。

Decorator(抽象装饰类):它也是抽象构件类的子类,用于给具体构件增加职责,但是具体职责在其子类中实现。它维护一个指向抽象构件对象的引用,通过该引用可以调用装饰之前构件对象的方法,并通过其子类扩展该方法,以达到装饰的目的。

ConcreteDecorator(具体装饰类):它是抽象装饰类的子类,负责向构件添加新的职责。每一个具体装饰类都定义了一些新的行为,它可以调用在抽象装饰类中定义的方法,并可以增加新的方法用以扩充对象的行为。

2.2 案例

示例代码:

  1. #include <iostream>
  2. #include <string>
  3. using namespace std;
  4. // Component(抽象构件),它是具体构件和抽象装饰类的共同父类,手机
  5. class Phone
  6. {
  7. public:
  8. virtual void show() = 0;
  9. };
  10. // ConcreateComponent(具体构件),iphone
  11. class Iphone : public Phone
  12. {
  13. public:
  14. Iphone(string kind)
  15. {
  16. this->kind = kind;
  17. }
  18. virtual void show()
  19. {
  20. cout << "我掏出了 iphone-" << kind << "秀了秀" << endl;
  21. }
  22. private:
  23. string kind;
  24. };
  25. // ConcreateComponent(具体构件),小米手机
  26. class MiPhone : public Phone
  27. {
  28. public:
  29. MiPhone(string kind)
  30. {
  31. this->kind = kind;
  32. }
  33. virtual void show()
  34. {
  35. cout << "我掏出了 小米-" << kind << "秀了秀" << endl;
  36. }
  37. private:
  38. string kind;
  39. };
  40. // Decorator(抽象装饰类),手机装饰器
  41. class DecoratorPhone : public Phone
  42. {
  43. public:
  44. DecoratorPhone()
  45. {
  46. }
  47. DecoratorPhone(Phone* phone)
  48. {
  49. this->phone = phone;
  50. }
  51. virtual void show()
  52. {
  53. this->phone->show();
  54. }
  55. private:
  56. Phone* phone;
  57. };
  58. // ConcreteDecorator(具体装饰类),贴膜装饰器
  59. class DecoratorPhoneMo : public DecoratorPhone
  60. {
  61. public:
  62. DecoratorPhoneMo(Phone* phone)
  63. {
  64. this->phone = phone;
  65. }
  66. void AddMo()
  67. {
  68. cout << "装饰:手机贴膜" << endl;
  69. }
  70. virtual void show()
  71. {
  72. this->phone->show();
  73. AddMo();
  74. }
  75. private:
  76. Phone* phone;
  77. };
  78. // ConcreteDecorator(具体装饰类),皮套装饰器
  79. class DecoratorPhoneTao : public DecoratorPhone
  80. {
  81. public:
  82. DecoratorPhoneTao(Phone* phone)
  83. {
  84. this->phone = phone;
  85. }
  86. void AddTao()
  87. {
  88. cout << "装饰:手机外套" << endl;
  89. }
  90. virtual void show()
  91. {
  92. this->phone->show();
  93. AddTao();
  94. }
  95. private:
  96. Phone* phone;
  97. };
  98. int main(void)
  99. {
  100. Phone *phone = NULL;
  101. DecoratorPhone* hasMoPhone = NULL;
  102. DecoratorPhone* hasTaoPhone = NULL;
  103. DecoratorPhone* hasMoTaoPhone = NULL;
  104. // 定义一个 iphone12
  105. phone = new Iphone("12");
  106. // 给 iphone12 贴膜
  107. hasMoPhone = new DecoratorPhoneMo(phone);
  108. // 给 iphone12 加上皮套
  109. hasTaoPhone = new DecoratorPhoneTao(phone);
  110. hasMoPhone->show();
  111. hasTaoPhone->show();
  112. // 给有皮套的 iphone12 再贴膜
  113. hasMoTaoPhone = new DecoratorPhoneMo(hasTaoPhone);
  114. hasMoTaoPhone->show();
  115. delete hasMoPhone;
  116. delete hasTaoPhone;
  117. delete hasMoTaoPhone;
  118. delete phone;
  119. return 0;
  120. }

运行结果:

2.3 优缺点

(1) 对于扩展一个对象的功能,装饰模式比继承更加灵活性,不会导致类的个数急剧增加。

(2)可以通过一种动态的方式来扩展一个对象的功能,从而实现不同的行为。

(3) 可以对一个对象进行多次装饰

(4)具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,原有类库代码无须改变,符合“开闭原则”。

缺点:

(1) 使用装饰模式进行系统设计时 将产生很多小对象,大量小对象的产生势必会占用更多的系统资源,影响程序的性能。

2.4 适用场景

(1)动态、透明的方式给单个对象添加职责。

(2) 当不能采用继承的方式对系统进行扩展或者采用继承不利于系统扩展

和维护时可以使用装饰模式。

3 外观模式

外观模式即 Facade 模式,是由 GoF 提出的 23 种设计模式中的一种。Facade 模式为一组具有类似功能的类群,比如类库,子系统等等,提供一个一致的简单的界面。这个一致的简单的界面被称为 Facade 。

根据迪米特法则,如果两个类不直接通信,那么这两个类就不应该发生直接的相互作用。

外观模式就是将复杂的子类系统(SubSystem)抽象到同一个接口(Façade)进行管理,外界只需要通过此接口(Façade)与子类系统(SubSystem)进行交互,而不需要直接与复杂的子类系统(SubSystem)进行交互。

3.1 模式中的角色和职责

Façade(外观角色):为调用方 提供简单的调用接口

SubSystem(子系统角色):功能提供者。指提供功能的类群(模块或子系

统)。

3.2 案例

示例

根据类图,实现家庭影院外观模式应用。

实现KTV模式:电视打开,灯关掉,音响打开,麦克风打开,dvd打开。

实现游戏模式:电视打开,音响打开,游戏机打开。

示例代码

  1. #include <iostream>
  2. using namespace std;
  3. class TV
  4. {
  5. public:
  6. void On()
  7. {
  8. cout << "电视打开了" << endl;
  9. }
  10. void Off()
  11. {
  12. cout << "电视关闭了" << endl;
  13. }
  14. };
  15. class DVD
  16. {
  17. public:
  18. void On()
  19. {
  20. cout << "DVD打开了" << endl;
  21. }
  22. void Off()
  23. {
  24. cout << "DVD关闭了" << endl;
  25. }
  26. };
  27. class Xbox
  28. {
  29. public:
  30. void On()
  31. {
  32. cout << "Xbox打开了" << endl;
  33. }
  34. void Off()
  35. {
  36. cout << "Xbox关闭了" << endl;
  37. }
  38. };
  39. class MikePhone
  40. {
  41. public:
  42. void On()
  43. {
  44. cout << "MikePhone打开了" << endl;
  45. }
  46. void Off()
  47. {
  48. cout << "MikePhone关闭了" << endl;
  49. }
  50. };
  51. class Light
  52. {
  53. public:
  54. void On()
  55. {
  56. cout << "Light打开了" << endl;
  57. }
  58. void Off()
  59. {
  60. cout << "Light关闭了" << endl;
  61. }
  62. };
  63. class HomePlayer
  64. {
  65. public:
  66. //ktv模式的接口
  67. void doKTV()
  68. {
  69. light.Off();
  70. tv.On();
  71. dvd.On();
  72. }
  73. //游戏模式的接口
  74. void doGame()
  75. {
  76. tv.On();
  77. xbox.On();
  78. }
  79. Light light;
  80. TV tv;
  81. MikePhone mike;
  82. Xbox xbox;
  83. DVD dvd;
  84. };
  85. int main(void)
  86. {
  87. HomePlayer hp;
  88. cout << "进入ktv模式" << endl;
  89. hp.doKTV();
  90. cout << "进入游戏模式" << endl;
  91. hp.doGame();
  92. return 0;
  93. }

运行结果

3.3 优缺点

优点

(1)它对客户端屏蔽了子系统组件,减少了客户端所需处理的对象数目,并

使得子系统使用起来更加容易。通过引入外观模式,客户端代码将变得很简

**单,与之关联的对象也很少。 **

(2)它实现了子系统与客户端之间的松耦合关系,这使得子系统的变化不会

**影响到调用它的客户端,只需要调整外观类即可。 **

(3)一个子系统的修改对其他子系统没有任何影响。

缺点

(1)不能很好地限制客户端直接使用子系统类,如果对客户端访问子系统类

做太多的限制则减少了可变性和灵活性。

(2)如果设计不当,增加新的子系统可能需要修改外观类的源代码,违背了

开闭原则。

3.4 适用场景

(1)复杂系统需要简单入口使用。

(2)客户端程序与多个子系统之间存在很大的依赖性。

(3)在层次化结构中,可以使用外观模式定义系统中每一层的入口,层与

层之间不直接产生联系,而通过外观类建立联系,降低层之间的耦合度

4 适配器模式

适配器模式(Adapter Pattern):将一个类的接口转换成客户希望的另外一个接口。

适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

4.1 模式中的角色和职责

Target(目标抽象类):目标抽象类 定义客户所需接口,可以是一个抽象类或接口,也可以是具体类

Adaptee(适配者类):适配者即被适配的角色,它 定义了一个已经存在的接口,这个接口需要适配,适配者类 一般是一个具体类,包含了客户希望使用的业务方法,在某些情况下可能没有适配者类的源代码。

Adapter(适配器类):适配器可以调用另一个接口,作为一个转换器,对 Adaptee 和 Target 进行适配,适配器类是适配器模式的核心,在对象适配器中,它 **通过继承 Target 并关联一个 Adaptee 对象使二者产生联系。 **

4.2 案例

示例代码:

  1. #include <iostream>
  2. using namespace std;
  3. // Target(目标抽象类),5V 电压
  4. class V5
  5. {
  6. public:
  7. virtual void useV5() = 0;
  8. };
  9. // Adaptee(适配者类),220V 电压
  10. class V220
  11. {
  12. public:
  13. virtual void useV220()
  14. {
  15. cout << "用 220V 电压进行充电" << endl;
  16. }
  17. };
  18. // Adapter(适配器类),手机充电器
  19. class PhoneChargeAdapter : public V5
  20. {
  21. public:
  22. virtual void useV5()
  23. {
  24. cout << "手机充电器对电压进行适配" << endl;
  25. m_v220.useV220();
  26. }
  27. private:
  28. V220 m_v220;
  29. };
  30. class Phone
  31. {
  32. public:
  33. Phone()
  34. {
  35. v5 = new PhoneChargeAdapter;
  36. }
  37. ~Phone()
  38. {
  39. if(v5 != NULL)
  40. delete v5;
  41. }
  42. void charge()
  43. {
  44. cout << "手机进行充电" << endl;
  45. v5->useV5();
  46. }
  47. private:
  48. V5* v5;
  49. };
  50. int main(void)
  51. {
  52. Phone iphone;
  53. iphone.charge();
  54. return 0;
  55. }

运行结果:

4.3 优缺点

优点:

(1)将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者

类,无须修改原有结构。

(2)增加了类的透明性和复用性,将具体的业务实现过程封装在适配者类

中,对于客户端类而言是透明的,而且提高了适配者的复用性,同一个适配者

类可以在多个不同的系统中复用。

(3)灵活性和扩展性都非常好,可以很方便地更换适配器,也可以在不修改

原有代码的基础上增加新的适配器类,完全符合“开闭原则”。

缺点:

适配器中置换适配者类的某些方法比较麻烦

4.4 适用场景

(1)系统需要使用一些现有的类,而这些类的接口(如方法名)不符合系

统的需要,甚至没有这些类的源代码。

(2)想创建一个可以重复使用的类,用于与一些彼此之间没有太大关联的

一些类,包括一些可能在将来引进的类一起工作

C++ 设计模式 3:结构型模式的更多相关文章

  1. Go语言实现的23种设计模式之结构型模式

    摘要:本文主要聚焦在结构型模式(Structural Pattern)上,其主要思想是将多个对象组装成较大的结构,并同时保持结构的灵活和高效,从程序的结构上解决模块之间的耦合问题. 本文分享自华为云社 ...

  2. Java设计模式之结构型模式

    结构型模式,共七种:适配器模式.装饰器模式.代理模式.外观模式.桥接模式.组合模式.享元模式. 一.适配器模式: 意图: 将一个类的接口转换成客户希望的另外一个接口.Adapter 模式使得原本由于接 ...

  3. Java经典23种设计模式之结构型模式(一)

    结构型模式包含7种:适配器模式.桥接模式.组合模式.装饰模式.外观模式.享元模式.代理模式. 本文主要介绍适配器模式和桥接模式. 一.适配器模式(Adapter) 适配器模式事实上非常easy.就像手 ...

  4. GoF的23种设计模式之结构型模式的特点和分类

    结构型模式描述如何将类或对象按某种布局组成更大的结构.它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象. 由于组合关系或聚合关系比继承关系耦合度低,满足 ...

  5. GoF23种设计模式之结构型模式之桥接模式

    一.概述         将类的抽象部分与实现分部分离开来,使它们都可以独立地变化. 二.适用性 1.你不希望在抽象和实现之间有一个固定的绑定关系的时候.例如:在程序运行时实现部分应可以被选择或切换. ...

  6. GoF23种设计模式之结构型模式之组合模式

    一.概述 将对象组合成树型结构以表示“部分--整体”的层次关系.组合模式使得用户对单个对象和组合对象的使用具有一致性. 二.适用性 1.你想表示对象的部分--整体层次结构的时候. 2.你希望用户忽略组 ...

  7. GoF23种设计模式之结构型模式之外观模式

    一.概述         为子系统中的一组接口提供一个一致的界面,外观模式定义了一个高层接口,这个接口使得这一子系统更加容易使用. 二.适用性 1.当你要为一个复杂子系统提供一个简单接口的时候.子系统 ...

  8. GoF23种设计模式之结构型模式之享元模式

    一.概述  运用共享技术有效地支持大量细粒度的对象. 二.适用性 1.当一个应用程序使用了大量的对象的时候. 2.由于使用大量的独享而造成很大的存储开销的时候. 3.对象的大多数状态都可变为外部状态的 ...

  9. 设计模式(7)-结构型模式-Bridge模式

    2.结构性模式 2.2  BRIDGE模式 别名:handle/body 这个模式体现了组合相对于继承的优势. 2.2.1动机 当一个抽象可能有多个实现时,通经常使用继承来协调它们.抽象类定义对该抽象 ...

  10. 设计模式-Proxy(结构型模式)

    以下代码来源: 设计模式精解-GoF 23种设计模式解析附C++实现源码 //Proxy.h #pragma once class Subject { public: virtual ~Subject ...

随机推荐

  1. STM32之旅5——IWDG

    STM32之旅5--IWDG stm32有两个看门狗,一个独立看门狗(IWDG).一个窗口看门狗(WWDG):独立看门狗是时钟源是内部的40kHz的低速时钟,即使主频出问题了,独立看门狗也不会受到影响 ...

  2. JS中实现Trim(),TrimStart(),TrimEnd() 的方法

    //去除字符串头尾空格或指定字符 String.prototype.Trim = function (c) { if (c == null || c == "") { var st ...

  3. 【C语言高级编程】你见过长度为0的数组吗?管你信不信,看就完了!

    一.什么是零长度数组 零长度数组就是长度为0的数组. ANSI C 标准规定:定义一个数组时,数组的长度必须是一个常数,即数组的长度在编译的时候是确定的.在ANSI C 中定义一个数组的方法如下: 类 ...

  4. 用CentOS 7自制Vagrant Box文件

      写在前面 利用vagrant保持开发生产环境一致是一个很好的方法,不过vagrant官网上的box文件下载是真的很慢,因此,这里教大家如何自制box文件. 这篇文章你会接触到: vagrant使用 ...

  5. centos8安装php7.4

    一,下载php7.4 1,官方网站: https://www.php.net/ 2,下载 [root@yjweb source]# wget https://www.php.net/distribut ...

  6. 【API管理 APIM】APIM集成内部VNet后,自我访问出现(Unable to connect to the remote server)问题,而Remote Server正是APIM它自己

    问题描述 在使用APIM配置内部VNET后,如API-1正常配置访问后端服务器的一个接口,而API-2则是通过调用APIM中的API-1来作为backendUrl,会出现500错误. 经过测试,目前这 ...

  7. python第一章:基础

    1.数学操作符: 数学操作符与数学基本类似 最高级:** 第二级:*././/.% 第三级:+ .- 遵循从左到右的顺序 如果想改变优先级 可以使用括号,比如:(3+2)*(5-4)=5*1=5 2. ...

  8. git学习(五) git diff操作

    git diff操作 git diff用于比较差异: git diff 不加任何参数 用于比较当前工作区跟暂存区的差异 git diff --cached 或者--staged 对比暂存区(git a ...

  9. mongodb 数据操作CRUD

    链接到mongo 新建超级用户 上文中我们提到mongo用户库表管理.为了方便我们先新建一个root权限的用户. db.createUser({user:'dbadmin',pwd:'123456', ...

  10. SAP ABAP: 把内表数据以excel或csv格式,通过前台或者后台的方式上传至FTP服务器

    今天接到一个FTP的需求,就是每天晚上把当天某个报表的数据自动保存excel上传到FTP服务器. SAP已经有现成的FTP函数使用,可以通过函数的方式来实现,实现前先准备一些数据: User:登录FT ...