23个小案例带你吃透23种设计模式(二)

简介: 23个小案例带你吃透23种设计模式

🚀四、建造者模式

1. 什么是建造者模式

Bulider Pattern,建造者模式,也叫做生成器模式,是一种对象创建型模式。建造者模式用于创建具有多个部件的复合对象,并隐藏了复合对象的创建过程,不同的部件建造者(Builder生成器)有不同的建造方法。通过建造者模式实现了对象的构建和对象的表示的分离,也就是说,通过同样的构建过程(建造逻辑)可以创建出不同的表示(使用不同的建造者产生不同的建造方式)。

建造者模式中的4种角色:

  1. 抽象建造者角色Builder:为建造各个组件提供统一的抽象接口;
  2. 具体建造者角色ConcreteBuilder:实现抽象建造者提供的抽象接口,定义各个组件的建造方法,是组件建造的具体实施者;
  3. 指挥者Director:调用具体建造者来建造产品的各个组件,指挥者并不知道产品的具体信息,指挥者只负责规定并保证各个组件的建造过程和建造逻辑(指挥建造的过程,比如先装发动机再装轮子);
  4. 产品角色Product:被建造的复杂对象,包含组合对象的各个部件;

2. 建造者模式的实现

首先我们定义一个汽车产品类,这个汽车包含外壳、发动机、车轮三个部件,并且汽车产品类中应该包含设置各个部件和获取各个部件的方法。汽车产品类是我们最终要建造的目标,是客户的需求。

1. //最终产品类:汽车
2. class CarProduct
3. {
4. public:
5.  void set_shell(string shell) //建造汽车外壳
6.  {
7.    this->shell = shell;
8.  }
9.  void set_engine(string engine) //建造汽车发动机
10.   {
11.     this->engine = engine;
12.   }
13.   void set_whell(string whell) //建造汽车轮子
14.   {
15.     this->whell = whell;
16.   }
17.   //获取属性
18.   string get_shell()
19.   {
20.     return this->shell;
21.   }
22.   string get_engine()
23.   {
24.     return this->engine;
25.   }
26.   string get_whell()
27.   {
28.     return this->whell;
29.   }
30. private:
31.   string shell; //外壳
32.   string engine; //发动机
33.   string whell; //轮子
34. };

定义一个抽象的建造者基类,类中统一了建造部件的接口和返回产品成品的方法。

1. //建造者基类:抽象施工单位
2. class Builder
3. {
4. public:
5.  virtual void builder_shell() = 0; //汽车外壳的建造方式接口
6.  virtual void builder_engine() = 0; //发动机的建造方式
7.  virtual void builder_whell() = 0; //车轮的建造方式
8.  virtual CarProduct* get_car() = 0; //返回建造好的汽车产品
9. };

定义具体建造者类,具体建造者类是产品部件的具体建造者,也就是汽车的生产商,我们定义两个汽车生产商,一个厂家生产卡车,一个厂家生产火车。

1. //具体的建造者:具体施工单位、具体的建造方式
2. class TruckBuilder : public Builder //卡车建造商
3. {
4. private:
5.  CarProduct* m_car;
6. public:
7.  TruckBuilder()
8.  {
9.    this->m_car = new CarProduct; 
10.   }
11.   virtual void builder_shell()
12.   {
13.     this->m_car->set_shell("卡车外壳");
14.   }
15.   virtual void builder_engine()
16.   {
17.     this->m_car->set_engine("卡车发动机");
18.   }
19.   virtual void builder_whell()
20.   {
21.     this->m_car->set_whell("卡车轮子");
22.   }
23.   virtual CarProduct* get_car()
24.   {
25.     return this->m_car;
26.   }
27. };
28. class TrainBuilder : public Builder //火车建造商
29. {
30. private:
31.   CarProduct* m_car;
32. public:
33.   TrainBuilder()
34.   {
35.     this->m_car = new CarProduct;
36.   }
37.   virtual void builder_shell()
38.   {
39.     this->m_car->set_shell("火车外壳");
40.   }
41.   virtual void builder_engine()
42.   {
43.     this->m_car->set_engine("火车发动机");
44.   }
45.   virtual void builder_whell()
46.   {
47.     this->m_car->set_whell("火车轮子");
48.   }
49.   virtual CarProduct* get_car()
50.   {
51.     return this->m_car;
52.   }
53. };

最后,应该定义一个指挥者,指挥者是汽车的设计师,它负责规划建造汽车的逻辑步骤,指挥汽车厂家(具体建造者)干活,而具体的工作有汽车厂家去干,所以指挥者不关心汽车的具体细节,只负责设计建造汽车各个部件的逻辑关系,比如先制造汽车外壳,然后安装发动机,最后安装车轮。

1. //指挥者:设计师,负责设计建造逻辑
2. class Director
3. {
4. public:
5.  Director(Builder* builder)
6.  {
7.    this->m_builder = builder;
8.  }
9.  //建造逻辑
10.   void builder_logic()
11.   {
12.     //1.先建造车的外壳
13.     this->m_builder->builder_shell();
14.     cout << "先建造车的外壳\t";
15.     //2.再安装发动机
16.     this->m_builder->builder_engine();
17.     cout << "再安装发动机\t";
18.     //3.最后安装车轮
19.     this->m_builder->builder_whell();
20.     cout << "最后安装车轮\n";
21.   }
22. private:
23.   Builder* m_builder;
24. };

最后是根据客户需求去建造汽车,假设客户需要一辆卡车。

1. int main()
2. {
3.  CarProduct* myCar = NULL;
4.  Builder* tempBuilder = NULL;
5.  Director* carDector = NULL;
6. 
7.  //需求:建造一辆卡车
8.  //首先找一个卡车建造商
9.  tempBuilder = new TruckBuilder;
10.   //把建造商交给指挥者(设计师)管理
11.   carDector = new Director(tempBuilder);
12.   //开始建造
13.   carDector->builder_logic();
14.   //获取产品  对象的建造逻辑和产品的表示分离
15.   myCar = tempBuilder->get_car();
16.   cout << "======产品信息======" << endl;
17.   cout << myCar->get_shell() << endl;
18.   cout << myCar->get_engine() << endl;
19.   cout << myCar->get_whell() << endl;
20.   cout << "====================" << endl;
21.   delete myCar;
22.   delete carDector;
23.   delete tempBuilder;
24. }

假如客户提出新需求,需要一辆火车,那么我们直接让指挥者去指挥火车厂商生产即可。

1. //新需求:需要一辆火车
2.  tempBuilder = new TrainBuilder;
3.  carDector = new Director(tempBuilder);
4.  carDector->builder_logic();
5.  myCar = tempBuilder->get_car();
6.  cout << "======产品信息======" << endl;
7.  cout << myCar->get_shell() << endl;
8.  cout << myCar->get_engine() << endl;
9.  cout << myCar->get_whell() << endl;
10.   cout << "====================" << endl;
11.   delete myCar;
12.   delete carDector;
13.   delete tempBuilder;

3. 建造者模式和工厂模式对比

建造者模式和工厂模式的区别是:工厂模式强调的是结果,不考虑对象的建造过程,只关注产生一个客户所需要的结果。比如,客户需要一辆大众汽车,那么就直接使用大众汽车工厂来生产一辆大众汽车,只关注大众汽车这个结果,不关心汽车外壳、发动机、轮子等部件的建造过程。建造者模式强调的是建造过程,要关注每一个部件的建造方式,以及各个部件的建造逻辑,最终组合出需要的对象。

🚀五、原型模式

1. 什么是原型模式

Prototype Pattern,原型模式是一种对象创建型模式,它采取复制原型对象的方法来创建对象的实例,所以称之为Clone,被复制出来的对象具有和原型一摸一样的数据,并且在通过Clone创造另一个一模一样的对象时,不需要知道创造的过程。根据对象克隆深度层次的不同,有浅度克隆与深度克隆。

2. 原型模式的实现

既然原型模式是复制一个一模一样的对象,那么就一定要注意潜在的深拷贝浅拷贝问题。

1. #include <iostream>
2. using namespace std;
3. 
4. #include <String>
5. 
6. class MyString
7. {
8. public:
9.  virtual MyString* Clone() = 0;
10.   virtual void print_str() = 0;
11. };
12. 
13. class Hello : public MyString
14. {
15. private:
16.   int len;
17.   string str;
18. public:
19.   Hello()
20.   {
21.     this->len = 5;
22.     this->str = "Hello";
23.   }
24.   virtual MyString* Clone()
25.   {
26.     Hello* temp = new Hello;
27.     *temp = *this;
28.     return temp;
29.   }
30.   virtual void print_str()
31.   {
32.     cout << "len:" << len << "   str:" << str << endl;
33.   }
34. };
35. 
36. int main()
37. {
38.   MyString* h1 = new Hello;
39.   h1->print_str();
40.   MyString* h2 = h1->Clone();
41.   h2->print_str();
42. 
43.   delete h1;
44.   delete h2;
45. 
46.   system("pause");
47.   return 0;
48. }

3. 创建型设计模式总结

顾名思义,创建型设计模式就是处理对象创建过程的设计模式。创建型模式主要是将系统所需要的用到的具体类封装起来,在内部实现这些具体类的创建和结合,并对外隐藏这个过程细节。创建型设计模式主要包括:

  • 单例模式
  • 简单工厂模式
  • 工厂模式
  • 抽象工厂模式
  • 建造者模式
  • 原型模式

其中,简单工厂模式因为不符合开闭原则,它不属于标准的23种设计模式。

🚀六、代理模式

1. 什么是代理模式

Proxy Pattern,代理模式,是构造型的设计模式之一,它可以为其他对象提供一种代理来控制对这个对象的访问。所谓的代理,就是指一个具有与被代理对象(代理元)相同接口的类,客户端只有通过Proxy来实现与被代理类的交互,并且在交互过程中 ,代理可以增加其它操作。

代理可以分为多个种类

  • 远程代理:可以隐藏一个对象在不同地址空间的事实,可以理解为将工作委托给远程的代理(服务器)来完成;
  • 虚拟代理:通过代理来存放需要很长时间实例化的对象;
  • 安全代理:用来控制真实对象的访问权限;
  • 智能引用:当调用真实对象时,代理处理另外一些事;

在代理模式中,有四种角色

  • 抽象主题角色subject:真实主题与代理主题的共同接口,并供客户端使用;
  • 真实主题角色RealSubject: 定义了代理角色所代表的真实实体。
  • 代理主题角色Proxy:含有一个引用,使得代理可以访问真实主题角色,并提供一个和抽象主题角色相同的接口,以便于代理角色可以用来替代真是主题角色。代理角色通常在将客户端调用传递给真实主题对象之前或者之后执行某些操作,而不是单纯返回真实的对象,也就是说代理可以执行真是主题角色之外的其他操作。

2. 代理模式的案例

假设现在有一个客户需要买车,他可以去实体店买,也可以在瓜子二手车买,瓜子二手车就是一个代理,它可以销售实体店的车,并提供节假日打折活动。

1. #include <iostream>
2. using namespace std;
3. 
4. //抽象主题角色
5. class CarStore
6. {
7. public:
8.  virtual void sail_Car() = 0;
9. };
10. 
11. //真实主题角色
12. class ReilCarStore : CarStore //实际汽车销售店铺
13. {
14. public:
15.   virtual void sail_Car()
16.   {
17.     cout << "实体店铺直接卖出汽车" << endl;
18.   }
19. };
20. 
21. //代理
22. class GZproxy : public CarStore //瓜子二手车是一个代理商
23. {
24. public:
25.   /*
26.   virtual void sail_Car()
27.   {
28.     cout << "瓜子二手车直营(自己卖)" << endl;
29.   }
30.   */
31.   virtual void sail_Car()
32.   {
33.     ReilCarStore cartore;
34.     cartore.sail_Car(); //表面是瓜子二手车卖车,实际上销售的是实体店的车
35.     discount(); //扩充了实体店卖车的形式和功能
36.   }
37.   void discount()
38.   {
39.     cout << "双11活动打折" << endl;
40.   }
41. };
42. 
43. //客户
44. int main()
45. {
46.   GZproxy dd;
47.   dd.sail_Car();
48. 
49.   system("pause");
50.   return 0;
51. }

🚀七、装饰模式

1. 什么是装饰模式

Decorator Pattern,装饰模式,也叫做包装模式,是结构型模式的一种。装饰模式动态的给一个对象增加了额外的功能,并且这种扩充功能的方式对客户是透明的。装饰模式的具体实现就是把一些功能封装在一个个单独的子类中,并让这些子类包含要被装饰的对象,当有需要功能扩充的时候,客户就可以有选择的通过装饰类来装饰某个对象。装饰模式可以理解为继承的一种替代,他比继承更加灵活,客户可以根据需要自由选择。

  • Component:被装饰的主体,定义了一个对象接口,可以给这些对象增加装饰功能;
  • ConcreteComponent:具体的对象,继承于Component;
  • Decorator:装饰者,继承于Component并从Component类的外部完成对Component类功能的扩充,并且Component类并不需要知道装饰者的存在;

2. 装饰模式案例

假设我们要设计一个游戏角色Hero,它最初只有跑步的技能,但是通过升级可以完成功能扩充,比如飞行,发射激光等。

首先定义一个Component角色,也就是要被装饰的主题的抽象接口。

1. class Hero
2. {
3. public:
4.  virtual void show_skill() = 0;
5. };

然后定义一个具体的要被装饰的对象ConcreteComponent

1. //初始的英雄:只能跑
2. class runHreo : public Hero
3. {
4. public:
5.  void run_skill()
6.  {
7.    cout << "只能跑的超人" << endl;
8.  }
9.  virtual void show_skill()
10.   {
11.     run_skill();
12.   }
13. };

最后添加两个装饰者,每个装饰者都可以完成对被装饰对象不同的功能扩充,一个装饰者可以为英雄角色扩充飞行技能,另一个装饰者可以为英雄角色扩充激光发射功能

1. //超人升级:新增了飞行技能
2. class flyHero : public Hero
3. {
4. private:
5.  Hero* m_hero;
6. public:
7.  flyHero(Hero* hero)
8.  {
9.    this->m_hero = hero;
10.   }
11.   void fly_skill()
12.   {
13.     cout << "拥有飞行技能" << endl;
14.   }
15.   virtual void show_skill()
16.   {
17.     this->m_hero->show_skill();
18.     fly_skill();
19.   }
20. };
21. 
22. //超人升级:可以发射激光
23. class laserHero : public Hero
24. {
25. private:
26.   Hero* m_hero;
27. public:
28.   laserHero(Hero* hero)
29.   {
30.     this->m_hero = hero;
31.   }
32.   void laser_skill()
33.   {
34.     cout << "可以发射激光" << endl;
35.   }
36.   virtual void show_skill()
37.   {
38.     this->m_hero->show_skill();
39.     laser_skill();
40.   }
41. };

最后是客户需求,首先我们让一个英雄角色从跑步到飞行再到激光发射,一步步的获取技能

1. int main()
2. {
3.  //创建一个超人角色
4.  Hero* myHero1 = NULL;
5. 
6.  cout << "*********第一个英雄*********" << endl;
7.  //初始功能只有跑
8.  cout << "======初始形态======" << endl;
9.  myHero1 = new runHreo;
10.   myHero1->show_skill();
11. 
12.   //功能增强:升级飞行技能
13.   cout << "======第一次升级======" << endl;
14.   Hero* myHero2 = new flyHero(myHero1);
15.   myHero2->show_skill();
16. 
17.   //再次升级:可以放大招
18.   cout << "======第二次升级======" << endl;
19.   Hero* myHero3 = new laserHero(myHero2);
20.   myHero3->show_skill();
21. 
22.   delete myHero1;
23.   delete myHero2;
24.   delete myHero3;
25. 
26.   system("pause");
27.   return 0;
28. }

然后我们在创建一个英雄角色,它可以直接获取激光功能

1. {
2.     cout << "*********第二个英雄*********" << endl;
3.  cout << "======初始形态======" << endl;
4.  myHero1 = new runHreo;
5.  myHero1->show_skill();
6. 
7.  //直接获取激光技能
8.  cout << "======升级======" << endl;
9.  myHero3 = new laserHero(myHero1);
10.   myHero3->show_skill();
11.   //增强功能可以自由组合
12. }

通过这两个英雄角色我们可以看到,客户可以根据需求任意组合对待装饰对象的功能扩充。

🚀八、适配器模式

1. 什么是适配器模式

Adapter Pattern,构造型模式之一,也叫做变压器模式和装饰模式都是一种包装模式(Wrapper),通过适配器模式可以改变现有类的接口形式。适配器模式可以将一个类的接口转换成客户希望的另一种形式的接口,使得原本因接口不兼容而无法工作的类可以一起工作,适用于双方都不适合修改的场景。

  • Target:客户期待的接口,可以是抽象类或者接口;
  • Adapter:适配器,对Target和Adaptee进行适配,通过在内部包装一个Adaptee对象,来把源接口转换成目标接口;
  • Adaptee:适配者,也就是需要被适配的类;

2. 适配器模式案例

假设我们现在只有一个RS232标准的接口,但是客户需求一个TTL标准的接口,这时就可以通过一个适配器把RS232接口适配成TTL电平标准。

1. #include <iostream>
2. using namespace std;
3. 
4. //Target:客户需要一个TTL电平接口
5. class TTL
6. {
7. public:
8.  virtual void get_ttl() = 0;
9. };
10. 
11. //Adaptee:现在只有一个RS232接口
12. class RS232
13. {
14. public:
15.   void get_rs232()
16.   {
17.     cout << "RS232接口" << endl;
18.   }
19. };
20. 
21. //Adapter:适配器
22. class adapter : public TTL
23. {
24. private:
25.   RS232* m_232;
26. public:
27.   adapter(RS232* m_232)
28.   {
29.     this->m_232 = m_232;
30.   }
31.   void adapter232_to_ttl()
32.   {
33.     cout << "适配器:将RS232转换为TTL" << endl;
34.   }
35.   virtual void get_ttl()
36.   {
37.     this->m_232->get_rs232();
38.     adapter232_to_ttl();
39.     cout << "ttl电平接口" << endl;
40.   }
41. };
42. 
43. int main()
44. {
45.   RS232* my232 = NULL;
46. 
47.   adapter* ad = NULL;
48. 
49.   //现有一个RS232电平接口
50.   my232 = new RS232;
51.   //需求是TTL接口,所以创建一个适配器
52.   my232->get_rs232();
53.   cout << "=========" << endl;
54.   ad = new adapter(my232);
55.   ad->get_ttl();
56. 
57.   delete my232;
58.   delete ad;
59. 
60.   system("pause");
61.   return 0;
62. }

🚀九、组合模式

1. 什么是组合模式

Composite Pattern,组合模式,是一种结构型模式。

组合模式将对象组合成树形结构以表示“整体-部分”的层次结构,并使得用户对单个对象和组合对象的使用具有一致性。

组合模式构造了一个树形的对象结构,并且通过一个属性对象可以可以访问整棵树的对象。

组合模式的三种角色:

  • Component:抽象角色,代表树形结构的抽象结点,它定义了所有实现类的统一接口(属性、方法、行为),并提供了访问和管理子结点的接口;简言之,Component定义了Leaf和Composite共同的操作接口,比如增加子结点,删除子结点等行为,是一个抽象基类。
  • Leaf:叶子结点,叶子结点中没有子结点,类似于树中的叶子结点,或文件系统中的文件。
  • Composite :枝结点,可以存储子结点,并实现子结点的操作,类似于文件系统的文件夹。

2. 组合模式案例

首先定义一个抽象类,为Leaf和Composite提供统一接口,假设要做一个文件系统,文件夹(Composite)中可以放入文件和文件夹,文件(Leaf)中不可以放入任何子结点。

1. class Component
2. {
3. public:
4.  virtual void display() = 0; //显示当前文件或文件夹名称
5.  virtual void add(Component* node) = 0; //在当前文件夹增加一个文件或文件夹
6.  virtual void remove(Component* node) = 0; //在当前文件夹删除一个文件或文件夹
7.  virtual vector<Component*>* get_child() = 0; //获取文件夹下属文件或文件夹
8. };

定义一个文件类,文件中不可加入子结点

1. class Leaf : public Component
2. {
3. private:
4.  string name;
5. public:
6.  Leaf(string name)
7.  {
8.    this->name = name;
9.  }
10.   virtual void display()
11.   {
12.     cout << "Leaf: " << this->name << endl;
13.   }
14.   virtual void add(Component* node)
15.   {
16.     cout << "叶子结点,无法加入" << endl;
17.   }
18.   virtual void remove(Component* node)
19.   {
20.     cout << "叶子结点,无此操作" << endl;
21.   }
22.   virtual vector<Component*>* get_child()
23.   {
24.     cout << "叶子结点,无子结点" << endl;
25.     return NULL;
26.   }
27. };

定义一个文件夹类,可以加入问价或文件夹

1. class Composite : public Component
2. {
3. private:
4.  string name;
5.  vector<Component*>* vec;
6. public:
7.  Composite(string name)
8.  {
9.    this->name = name;
10.     vec = new vector<Component*>;
11.   }
12.   ~Composite()
13.   {
14.     if (vec != NULL)
15.     {
16.       delete vec;
17.       vec = NULL;
18.     }
19.   }
20.   virtual void display()
21.   {
22.     cout << "Composite: " << this->name << endl;
23.   }
24.   virtual void add(Component* node)
25.   {
26.     vec->push_back(node);
27.   }
28.   virtual void remove(Component* node)
29.   {
30.     for (vector<Component*>::iterator it = vec->begin(); it != vec->end(); it++)
31.     {
32.       if (*it == node)
33.       {
34.         vec->erase(it);
35.       }
36.     }
37.   }
38.   virtual vector<Component*>* get_child()
39.   {
40.     cout << "*" << this->name << " child: " << "*\n";
41.     for (vector<Component*>::iterator it = vec->begin(); it != vec->end(); it++)
42.     {
43.       (*it)->display();
44.     }
45.     return vec;
46.   }
47. };

客户端操作

1. int main()
2. {
3.  Component* root = NULL;
4.  Leaf* l1 = NULL;
5.  Leaf* l2 = NULL;
6.  Composite* dir = NULL;
7. 
8.  root = new Composite("/root");
9.  l1 = new Leaf("1.cpp");
10.   l2 = new Leaf("2.cpp");
11.   dir = new Composite("/home");
12. 
13.   root->add(dir);
14.   dir->add(l1);
15.   dir->add(l2);
16. 
17.   cout << "============" << endl;
18.   root->display();
19.   root->get_child();
20.   dir->get_child();
21.   cout << "============" << endl;
22. 
23.   delete dir;
24.   delete l2;
25.   delete l1;
26.   delete root;
27. 
28.   system("pause");
29.   return 0;
30. }

🚀十、桥接模式

1. 什么是桥接模式

Bridge Pattern,桥接模式,是一种结构型设计模式。

桥接模式基于类的最小设计原则,通过使用封装,聚合以及继承等行为来让不同的类承担不同的责任。它的主要特点是把抽象(abstraction)与行为实现(implementation)分离开来,从而可以保持各部分的独立性以及应对它们的功能扩展。

也就是说,通过桥接模式可以实现抽象部分与实现部分的解耦合,使得抽象和实现都可以独立的发生变化,当通过继承不能实现开闭原则的时候,就可以考虑桥接模式。比如,图形和颜色,把图形设计一个抽象类,颜色设计一个抽象类,然后根据需要的图形去实现图形类,并根据需要的颜色实现颜色类,通过两个抽象类就可以实现颜色和图形的组合。

  • Abstractions:抽象类接口,包含了一个对行为实现类Implementor的引用,也就是说二者具有关联关系。
  • Refined Abstraction:抽象类接口的实现类,实现了Abstractions中定义的接口,并可以调用Implementor中的方法(包含了Implementor的引用)。
  • Implementor:行为实现类接口,定义了一系列操作。
  • Concretelmp lementor:具体实现类,实现了Implementor中的操作。

2. 桥接模式案例

实现对不同图形上不同颜色,首先定义两个抽象类,颜色类和图形类

1. class Color
2. {
3. public:
4.  virtual void get_color() = 0;
5. };
6. 
7. class Graph
8. {
9. protected:
10.   Color* mGraphColor;
11. public:
12.   Graph(Color* mGraphColor)
13.   {
14.     this->mGraphColor = mGraphColor;
15.   }
16.   virtual void smear_color() = 0; //给图形上色
17. };

定义三个具体的颜色

1. class Red : public Color
2. {
3. public:
4.  virtual void get_color()
5.  {
6.    cout << "红色" << endl;
7.  }
8. };
9. 
10. class Blue : public Color
11. {
12. public:
13.   virtual void get_color()
14.   {
15.     cout << "蓝色" << endl;
16.   }
17. };
18. 
19. class Yellow : public Color
20. {
21. public:
22.   virtual void get_color()
23.   {
24.     cout << "黄色" << endl;
25.   }
26. };

实现具体的图形

1. class Circle : public Graph
2. {
3. public:
4.  Circle(Color* mGraphColor) : Graph(mGraphColor) {};
5.  virtual void smear_color()
6.  {
7.    cout << "圆形 + ";
8.    mGraphColor->get_color();
9.  }
10. };
11. 
12. class Triangle : public Graph
13. {
14. public:
15.   Triangle(Color* mGraphColor) : Graph(mGraphColor) {};
16.   virtual void smear_color()
17.   {
18.     cout << "三角形 + ";
19.     mGraphColor->get_color();
20.   }
21. };

客户端操作,为图形上色

1. int main()
2. {
3.  Color* m_color = NULL;
4. 
5.  m_color = new Red;
6.  Circle* m_circle = new Circle(m_color);
7.  m_circle->smear_color();
8. 
9.  delete m_color;
10.   m_color = new Blue;
11.   Triangle* m_triangle = new Triangle(m_color);
12.   m_triangle->smear_color();
13. 
14.   delete m_color;
15.   delete m_triangle;
16.   delete m_circle;
17. 
18.   system("pause");
19.   return 0;
20. }

🚀十一、外观模式

1. 什么是外观模式

Facade Pattern,外观模式,结构型设计模式之一。外观模式为一个子系统集合提供了一个一致的简单界面,并且外观包含了对各个子系统的引用,客户端可以通过这个外观来访问各个子系统。

外观模式中的角色:

  • Facade:外观类,作为调用接口,提供所有子系统的一致访问界面;
  • Clients:通过外观角色来访问各个子系统;
  • Subsystem:子系统,实现子系统的功能;

2. 外观模式案例

首先创建子系统集合,假设为电脑系统,定义显示屏子系统、主机子系统、键盘子系统

1. class Screen //子系统1
2. {
3. public:
4.  void get_attr()
5.  {
6.    cout << "显示屏子系统" << endl;
7.  }
8. };
9. 
10. class Host //子系统2
11. {
12. public:
13.   void get_attr()
14.   {
15.     cout << "主机子系统" << endl;
16.   }
17. };
18. 
19. class Keyboard //子系统3
20. {
21. public:
22.   void get_attr()
23.   {
24.     cout << "键盘子系统" << endl;
25.   }
26. };

如果客户不适应外观类,而是直接访问这个子系统,那么操作将会比较繁琐

1. void Func1()
2. {
3.  Screen* mScreen;
4.  Host* mHost;
5.  Keyboard* mKeyboard;
6. 
7.  mScreen = new Screen;
8.  mHost = new Host;
9.  mKeyboard = new Keyboard;
10. 
11.   mScreen->get_attr();
12.   mHost->get_attr();
13.   mKeyboard->get_attr();
14. }

要创建每个子系统对象,并逐个调用其方法来访问子系统。

创建一个外观类,它包含对所有子系统的引用

1. class Facade
2. {
3. private:
4.  Screen* mScreen;
5.  Host* mHost;
6.  Keyboard* mKeyboard;
7. public:
8.  Facade()
9.  {
10.     mScreen = new Screen;
11.     mHost = new Host;
12.     mKeyboard = new Keyboard;
13.   }
14.   ~Facade()
15.   {
16.     if (mScreen != NULL)
17.     {
18.       delete mScreen;
19.     }
20.     if (mHost != NULL)
21.     {
22.       delete mHost;
23.     }
24.     if (mKeyboard != NULL)
25.     {
26.       delete mKeyboard;
27.     }
28.   }
29.   void get_attr()
30.   {
31.     this->mScreen->get_attr();
32.     this->mHost->get_attr();
33.     this->mKeyboard->get_attr();
34.   }
35. };

通过外观来访问所有子系统

1. void Func2()
2. {
3.  Facade* f = new Facade;
4.  f->get_attr();
5.  delete f;
6. }

🚀十二、享元模式

1. 什么是享元模式

Flyweight Pattern,享元模式,结构型设计模式。

在面向对象系统的设计和实现中,创建对象是最常见的操作,这里就会有一个问题:如果一个应用程序使用了太多对象,就会造成很大的存储开销。特别是对于大量轻量级(细粒度)的对象,比如在文档编辑器的设计过程中,我们如果为每一个字母创建一个对象的时候可能会因为大量的对象而造成存储开销的浪费,例如一个字母a在文档中出现了10000次,而实际上我们可以让这10000个a共享一个对象,这种情况下,我们可以将对象状态分为内部状态和外部状态。在享元模式中可以共享(不会变化)的状态称为内部状态(Intrinsic State),而那些需要外部环境来设置的不能共享的内容称为外部状态(Extrinsic State),其中外部状态和内部状态是相互独立的,外部状态的变化不会引起内部状态的变化。内部状态直接存储在对象中,而外部状态(字母a的大小、字体)可以在适当时机作为参数传递给对象。享元模式的目的就是使用共享技术来实现大量细粒度对象的复用。

享元模式的角色:

  • Flyweight:抽象享元角色,定义了所有具体享元类的接口,通过这个接口可以传入外部状态并作用于外部状态;
  • FlyweightFactory:享元工厂,创建并管理享元对象;
  • ConcreteFlyweight:可共享的内部状态;
  • UnsharedConcreteFlyweight:可以不共享的享元子类;

2. 享元模式案例

假设一个字符串和一个标志作为一对,可以通过标志来确定字符串,比如只要传入标志1就得到字符串hello,也就是说,通过flag可以确定一个字符串。

1. class MyStr
2. {
3. protected:
4.  string str;
5. public:
6.  MyStr(string str)
7.  {
8.    this->str = str;
9.  }
10.   virtual void get_str() = 0;
11. };
12. 
13. class Hello : public MyStr
14. {
15. private:
16.   int id;
17. public:
18.   Hello(string str, int id) : MyStr(str)
19.   {
20.     this->id = id;
21.   }
22.   virtual void get_str()
23.   {
24.     cout << "id: " << id << "  对应的str:  " << str << endl;
25.   }
26. };

创建一个工厂

1. class Factory
2. {
3. private:
4.  map<int, Hello*> m;
5. public:
6.  ~Factory()
7.  {
8.    while (!m.empty())
9.    {
10.       Hello* tmp = NULL;
11.       map<int, Hello*>::iterator it = m.begin();
12.       tmp = it->second;
13.       m.erase(it);
14.       delete tmp;
15.     }
16.   }
17.   Hello* get_str(int id)
18.   {
19.     Hello* mtemp;
20.     map<int, Hello*>::iterator it;
21.     it = m.find(id);
22.     if (it == m.end())
23.     {
24.       string temp;
25.       cout << "该字符串未找到,请输入字符串:";
26.       cin >> temp;
27.       mtemp = new Hello(temp, id);
28.       m.insert(make_pair(id, mtemp));
29.       return mtemp;
30.     }
31.     else
32.     {
33.       return it->second;
34.     }
35.   }
36. };

客户端通过工厂获取享元对象,并传入一个标志

1. int main()
2. {
3.  Hello* h1 = NULL, * h2 = NULL;
4.  Factory* f = NULL;
5. 
6.  f = new Factory;
7. 
8.  h1 = f->get_str(1);
9.  h1->get_str();
10. 
11.   h2 = f->get_str(1);
12.   h2->get_str();
13. 
14.   delete h2;
15.   delete h1;
16.   delete f;
17. 
18.   system("pause");
19.   return 0;
20. }

🚀十三、模板模式

1. 什么是模板模式

Template Pattern,模板方法模式,是一种行为型模式。通过模板模式可以把特定步骤的算法接口定义在抽象基类中,通过子类继承对抽象算法进行不同的实现来达到改变算法行为的目的。通俗来讲就是,在抽象类中定义好算法步骤并统一接口,在子类中实现接口,这就实现了算法操作步骤和算法实现的解耦合。模板模式一般应用于,具有同样的操作步骤,但是这些操作的细节不同的场景。

  • AbstractClass:定义了算法的框架和步骤;
  • ConcreteClass:实现AbstractClass中的方法;

2. 模板模式的案例

我们把穿衣服看做一个固定流程,先穿外套,再穿裤子,最后穿鞋

1. class WearClothes //穿衣服
2. {
3. public:
4.  virtual void wear_coat() = 0;
5.  virtual void wear_pants() = 0;
6.  virtual void wear_shoe() = 0;
7. public:
8.  void wear_order() //穿衣服的顺序已经提前确定好了---模板
9.  {
10.     wear_coat(); //先穿外套
11.     wear_pants(); //再穿裤子
12.     wear_shoe(); //最后穿鞋
13.   }
14. };

然后定义穿睡衣,穿西装两个类,穿衣服的顺序都是固定的,但是穿的衣服有所不同

1. class WearSuit : public WearClothes //穿西装
2. {
3.  virtual void wear_coat()
4.  {
5.    cout << "穿西服外套" << endl;
6.  }
7.  virtual void wear_pants()
8.  {
9.    cout << "穿西服裤子" << endl;
10.   }
11.   virtual void wear_shoe()
12.   {
13.     cout << "穿小皮鞋" << endl;
14.   }
15. };
16. 
17. class WearPajamas : public WearClothes //穿睡衣
18. {
19.   virtual void wear_coat()
20.   {
21.     cout << "穿睡衣" << endl;
22.   }
23.   virtual void wear_pants()
24.   {
25.     cout << "穿睡裤" << endl;
26.   }
27.   virtual void wear_shoe()
28.   {
29.     cout << "穿拖鞋" << endl;
30.   }
31. };

最后客户端执行穿衣服操作

1. int main()
2. {
3.  WearClothes* wear = NULL;
4. 
5.  //穿西装应酬
6.  wear = new WearSuit;
7.  wear->wear_order();
8.  delete wear;
9. 
10.   //穿睡衣睡觉
11.   wear = new WearPajamas;
12.   wear->wear_order();
13.   delete wear;
14. 
15.   system("pause");
16.   return 0;
17. }

🚀十四、命令模式

1. 什么是命令模式

Command Pattern,命令模式,是一种行为型设计模式。命令模式就是把命令对象、命令的创建者、命令的调用者、命令的执行者(接收者)分离,首先看一个命令模式中的四个角色:

  • Command:抽象命令,定义了操作的接口,把命令封装成一个类,通过继承在ConcreteCommand中来实现具体的命令操作;
  • ConcreteCommand:具体命令,实现抽象命令的接口,是被命令调用者Invoker调用的主体,并且包含了一个对命令接收者Receiver的引用;
  • Receiver:命令的执行者,收到命令后执行相应的操作;
  • Invoker:命令的调用者,客户创建命令,并通过Incoker去调用命令,Invoker包含了对Command的引用,并负责去调用命令中的操作;

2. 命令模式的案例

以办理银行业务为例,首先创建一个银行职员作为命令接收者,他可以执行存款取款操作。

1. //Receiver
2. class Banker
3. {
4. public:
5.  void saving_money()
6.  {
7.    cout << "办理存款业务" << endl;
8.  }
9.  void withdraw_money()
10.   {
11.     cout << "办理取款业务" << endl;
12.   }
13. };

创建一个命令抽象类,并通过继承实现一个存款命令类和一个取款命令类,这两个具体命令方法分别可以调用命令执行者的存款操作和取款操作。

1. class Command
2. {
3. public:
4.  virtual void conduct_business() = 0;
5. };
6. 
7. class SaveCommand : public Command
8. {
9. private:
10.   Banker* bker;
11. public:
12.   SaveCommand(Banker* bker)
13.   {
14.     this->bker = bker;
15.   }
16.   virtual void conduct_business()
17.   {
18.     this->bker->saving_money();
19.   }
20. };
21. 
22. class WithdrowCommand : public Command
23. {
24. private:
25.   Banker* bker;
26. public:
27.   WithdrowCommand(Banker* bker)
28.   {
29.     this->bker = bker;
30.   }
31.   virtual void conduct_business()
32.   {
33.     this->bker->withdraw_money();
34.   }
35. };

创建一个命令调用者,它可以调用命令对象(命令调用者Invoker中包含了命令Command的引用,它可以调用命令,而具体命令ConcreteCommand中包含了命令接收者Receiver的引用,具体命令可以调用Receiver的操作,客户通过命令调用者Invoker来命令Receiver进行相应操作)。

1. //Invoker
2. class Manager
3. {
4. private:
5.  Command* com;
6. public:
7.  Manager(Command* com)
8.  {
9.    this->com = com;
10.   }
11.   void order()
12.   {
13.     com->conduct_business();
14.   }
15. };

最后客户端创建命令,并通过Invoker来调用命令,使Receiver执行相应操作

1. int main()
2. {
3.  Manager* m = NULL;
4.  Command* com = NULL;
5.  Banker* bker = NULL;
6. 
7.  bker = new Banker;
8.  com = new SaveCommand(bker); //存款命令
9.  m = new Manager(com);
10.   m->order();
11. 
12.   delete com;
13.   delete m;
14.   com = new WithdrowCommand(bker); //取款命令
15.   m = new Manager(com);
16.   m->order();
17. 
18.   delete m;
19.   delete com;
20.   delete bker;
21. 
22.   system("pause");
23.   return 0;
24. }

相关文章
|
2月前
|
设计模式 数据库连接 PHP
PHP中的设计模式:提升代码的可维护性与扩展性在软件开发过程中,设计模式是开发者们经常用到的工具之一。它们提供了经过验证的解决方案,可以帮助我们解决常见的软件设计问题。本文将介绍PHP中常用的设计模式,以及如何利用这些模式来提高代码的可维护性和扩展性。我们将从基础的设计模式入手,逐步深入到更复杂的应用场景。通过实际案例分析,读者可以更好地理解如何在PHP开发中应用这些设计模式,从而写出更加高效、灵活和易于维护的代码。
本文探讨了PHP中常用的设计模式及其在实际项目中的应用。内容涵盖设计模式的基本概念、分类和具体使用场景,重点介绍了单例模式、工厂模式和观察者模式等常见模式。通过具体的代码示例,展示了如何在PHP项目中有效利用设计模式来提升代码的可维护性和扩展性。文章还讨论了设计模式的选择原则和注意事项,帮助开发者在不同情境下做出最佳决策。
|
3月前
|
设计模式 Java
常用设计模式介绍~~~ Java实现 【概念+案例+代码】
文章提供了一份常用设计模式的全面介绍,包括创建型模式、结构型模式和行为型模式。每种设计模式都有详细的概念讲解、案例说明、代码实例以及运行截图。作者通过这些模式的介绍,旨在帮助读者更好地理解源码、编写更优雅的代码,并进行系统重构。同时,文章还提供了GitHub上的源码地址,方便读者直接访问和学习。
常用设计模式介绍~~~ Java实现 【概念+案例+代码】
|
5月前
|
设计模式 算法 Java
Java中的设计模式:实战案例分享
Java中的设计模式:实战案例分享
|
6月前
|
设计模式 Java 关系型数据库
Java设计模式--创建模式工厂--用披萨订购案例 详细讲解三种工厂模式
Java设计模式--创建模式工厂--用披萨订购案例 详细讲解三种工厂模式
|
6月前
|
设计模式 存储 安全
NFT佛萨奇矩阵公排合约系统开发|案例分析|设计模式
区块链目前面临的另一个挑战是可扩展性问题
|
6月前
|
设计模式
设计模式案例 (三)
设计模式案例 (三)
38 0
|
6月前
|
设计模式
设计模式案例(二)
设计模式案例(二)
51 0
|
6月前
|
设计模式
设计模式案例(一)
设计模式案例(一)
42 0
|
设计模式 Java 应用服务中间件
【设计模式——学习笔记】23种设计模式——职责链/责任链模式(Chain of Responsibility)(原理讲解+应用场景介绍+案例介绍+Java代码实现)
【设计模式——学习笔记】23种设计模式——职责链/责任链模式(Chain of Responsibility)(原理讲解+应用场景介绍+案例介绍+Java代码实现)
142 0
|
设计模式 算法 Java
【设计模式——学习笔记】23种设计模式——策略模式Strategy(原理讲解+应用场景介绍+案例介绍+Java代码实现)
【设计模式——学习笔记】23种设计模式——策略模式Strategy(原理讲解+应用场景介绍+案例介绍+Java代码实现)
99 0

热门文章

最新文章

  • 1
    C++一分钟之-设计模式:工厂模式与抽象工厂
    42
  • 2
    《手把手教你》系列基础篇(九十四)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-下篇(详解教程)
    46
  • 3
    C++一分钟之-C++中的设计模式:单例模式
    53
  • 4
    《手把手教你》系列基础篇(九十三)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-上篇(详解教程)
    37
  • 5
    《手把手教你》系列基础篇(九十二)-java+ selenium自动化测试-框架设计基础-POM设计模式简介(详解教程)
    61
  • 6
    Java面试题:结合设计模式与并发工具包实现高效缓存;多线程与内存管理优化实践;并发框架与设计模式在复杂系统中的应用
    56
  • 7
    Java面试题:设计模式在并发编程中的创新应用,Java内存管理与多线程工具类的综合应用,Java并发工具包与并发框架的创新应用
    40
  • 8
    Java面试题:如何使用设计模式优化多线程环境下的资源管理?Java内存模型与并发工具类的协同工作,描述ForkJoinPool的工作机制,并解释其在并行计算中的优势。如何根据任务特性调整线程池参数
    49
  • 9
    Java面试题:请列举三种常用的设计模式,并分别给出在Java中的应用场景?请分析Java内存管理中的主要问题,并提出相应的优化策略?请简述Java多线程编程中的常见问题,并给出解决方案
    105
  • 10
    Java面试题:设计模式如单例模式、工厂模式、观察者模式等在多线程环境下线程安全问题,Java内存模型定义了线程如何与内存交互,包括原子性、可见性、有序性,并发框架提供了更高层次的并发任务处理能力
    75