学习设计模式系列之五:原型模式

原型模式:

属于创建型设计模式,直接复制一个已存在的实例作为新实例。

背景补充:

         创建型的设计模式,都属于工厂,内部实现产生实例的方法,对外开放一个获得实例的接口,将产生实例的方法与客户分离,降低耦合度。

         使用原型模式,可以同时使用单例模式产生工厂实例,使用抽象工厂管理生产线,再使用原型模式返回对象。

核心实现:

实现拷贝构造函数。

适用场景:

实例非常复杂,设置非常麻烦,实例的初始化非常耗时;

试图获得实例的客户并没有设置参数的能力,或者不想花费时间去设置参数。

模型类图:

        

图 1 模型图

举例说明:

在本例中,同时使用了抽象工厂,单例模式和原型模式:

工厂实例是通过单例模式创建的;

客户需要的Tank实例是通过原型模式实现的;

对于客户与产品之间的对应是通过抽象工厂模式实现的。

 

图 2 举例类图

代码:

  1 #include <iostream>
  2 #include <vector>
  3 #include <stdlib.h>
  4 
  5 /***
  6 * @author:zanzan101
  7 */
  8 
  9 using namespace std;
 10 // 定义枚举类型
 11 enum tank_state
 12 {
 13     TANK_ATTACK,
 14     TANK_MOVE,
 15     TANK_MOVEATTACK,
 16     TANK_GARD,
 17     TANK_HOLD
 18 };
 19 enum tank_type
 20 {
 21     PRISMTANK,
 22     GRIZZLYTANK,
 23     RHINOHEAVYTANK
 24 };
 25 // 定义坦克类
 26 class Tank
 27 {
 28 protected:
 29     int _hp;
 30     int _damage;
 31     int _speed;
 32     int _state;
 33     int _type;
 34     int _pos_x;
 35     int _pos_y;
 36     bool _dead;
 37 public:
 38 
 39     // 为避免派生类对象造成内存泄漏,需要将析构函数设置为虚函数
 40     virtual ~Tank(){}
 41     virtual void set_param(int hp, int da, int sp, int st, int ty, int pos_x, int pos_y, bool dead)
 42     {
 43         _hp = hp;
 44         _damage = da;
 45         _speed = sp;
 46         _state = st;
 47         _type = ty;
 48         _pos_x = pos_x;
 49         _pos_y = pos_y;
 50         _dead = dead;
 51     }
 52     virtual void speaking() const =0;
 53 };
 54 
 55 class PrismTank: public Tank
 56 {
 57 private:
 58     char* _chinese_name;
 59 public:
 60     PrismTank():_chinese_name(0){}
 61 
 62     // 使用原型模式时,如果类对象不适合用浅拷贝的时候,必须实现深拷贝构造函数,如下所示:
 63     PrismTank(PrismTank& tank)
 64     {
 65         _hp = tank._hp;
 66         _damage = tank._damage;
 67         _speed = tank._speed;
 68         _state = tank._state;
 69         _type = tank._type;
 70         _pos_x = tank._pos_x;
 71         _pos_y = tank._pos_y;
 72         _dead = tank._dead;
 73         _chinese_name = new char[strlen(tank._chinese_name)+1];
 74         strcpy(_chinese_name, tank._chinese_name);
 75     }
 76     
 77     // 由于本类对象申请了堆内存,为避免内存泄漏,需要利用多态机制进行析构
 78     ~PrismTank()
 79     {
 80         if(_chinese_name)
 81             delete _chinese_name;
 82     }
 83     void set_param(int hp, int da, int sp, int st, int ty, int pos_x, int pos_y, bool dead, const char* name)
 84     {
 85         _hp = hp;
 86         _damage = da;
 87         _speed = sp;
 88         _state = st;
 89         _type = ty;
 90         _pos_x = pos_x;
 91         _pos_y = pos_y;
 92         _dead = dead;
 93         _chinese_name = new char[strlen(name)+1];
 94         strcpy(_chinese_name, name);
 95     }
 96     void speaking() const
 97     {
 98         cout<< "I'm a Prism Tank! My chinese name is "<< _chinese_name << " My param are hp: "<< _hp << " damage: "<< _damage <<endl;
 99     }
100 };
101 
102 class GrizzlyTank: public Tank
103 {
104 public:
105     // 如果类适合用浅拷贝,则无需实现拷贝构造函数,系统默认的拷贝构造函数就能完成浅拷贝任务。
106 
107     void speaking() const
108     {
109         cout<< "I'm a Grizzly Tank! My param are hp: "<< _hp << " damage: "<< _damage <<endl;
110     }
111 };
112 
113 class RhinoHeavyTank: public Tank
114 {
115 public:
116     RhinoHeavyTank(){}
117 
118     // 下面实现的拷贝构造函数,但其实际上默认的一样~
119     RhinoHeavyTank(RhinoHeavyTank& tank)
120     {
121         memcpy(this, &tank, sizeof(RhinoHeavyTank));
122     }
123 
124     void speaking() const
125     {
126         cout<< "I'm a Rhino Heavy Tank! My param are hp: "<< _hp << " damage: "<< _damage <<endl;
127     }
128 };
129 
130 // 使用单例模式创建工厂的实例
131 class Factory
132 {
133 private:
134 
135     // 只有静态常量数据才可以在类中直接初始化,例如,下面的初始化是合法的:
136     // static const int nothing = 0;
137 
138     // 然而,常量非静态数据则不可以直接初始化,例如,下面的初始化是非法的:
139     // const int nothing = 0;
140     
141     // 常量非静态数据需要在“构造函数成员初始值设定项列表”中初始化,例如,下面初始化是合法的:
142     // const int nothing;
143     // Factory(): nothing(0){}
144 
145     static Factory* _instance;
146     PrismTank _prism_tank;
147     GrizzlyTank _grizzly_tank;
148     RhinoHeavyTank _rhino_heavy_tank;
149     Factory()
150     {
151         // 初始化原型实例,这一次初始化之后,后面的实例就是由他们进行拷贝构造
152         _prism_tank.set_param(100, 160, 30, TANK_GARD, PRISMTANK, 0, 0, false, "光棱坦克");
153         _grizzly_tank.set_param(140, 60, 50, TANK_GARD, GRIZZLYTANK, 0, 0, false);
154         _rhino_heavy_tank.set_param(180, 180, 20, TANK_GARD, RHINOHEAVYTANK, 0, 0, false);
155     }
156 
157 
158 public:
159     // 获取单例的接口
160     static Factory* get_instance()
161     {
162         if(!_instance)
163             _instance = new Factory();
164         return _instance;
165     }
166 
167     // 使用抽象工厂模式设计的对客户的接口,即,客户只需提供型号,而与生产线分离
168     Tank* get_prototype_tank(int type)
169     {
170         switch(type)
171         {
172         // 对于每一种型号,使用原型模式设计对实例的初始化
173         case PRISMTANK:
174             return (Tank*)(new PrismTank(_prism_tank));
175         case GRIZZLYTANK:
176             return (Tank*)(new GrizzlyTank(_grizzly_tank));
177         case RHINOHEAVYTANK:
178             return (Tank*)(new RhinoHeavyTank(_rhino_heavy_tank));
179         default:
180             cout<< "ERROR: wrong type !" << endl;
181             return 0;
182         }
183     }
184 };
185 Factory* Factory::_instance = 0;
186 
187 // 设计客户
188 class Client
189 {
190 private:
191     Factory* _factory;
192 public:
193     Client()
194     {
195         _factory = Factory::get_instance();
196     }
197 
198     // 模拟演示对原型模式的调用方法
199     void do_something()
200     {
201         Tank* tank;
202 
203         // 客户调用工厂的接口,获得通过原型模式创建的实例
204         tank = _factory->get_prototype_tank(PRISMTANK);
205         tank->speaking();
206         delete tank;
207 
208         tank = _factory->get_prototype_tank(GRIZZLYTANK);
209         tank->speaking();
210         delete tank;
211 
212         tank = _factory->get_prototype_tank(RHINOHEAVYTANK);
213         tank->speaking();
214         delete tank;
215         // 可以看出来上面的tank实例都是拷贝自工厂中的成品对象
216     }
217 };
218 int _tmain(int argc, _TCHAR* argv[])
219 {
220     Client client;
221     client.do_something();
222 
223     system("pause");
224     return 0;
225 }

输出结果:

I'm a Prism Tank! My chinese name is 光棱坦克 My param are hp: 100 damage: 160
I'm a Grizzly Tank! My param are hp: 140 damage: 60
I'm a Rhino Heavy Tank! My param are hp: 180 damage: 180
请按任意键继续. . .
原文地址:https://www.cnblogs.com/zanzan101/p/3406334.html