原型模式:
属于创建型设计模式,直接复制一个已存在的实例作为新实例。
背景补充:
创建型的设计模式,都属于工厂,内部实现产生实例的方法,对外开放一个获得实例的接口,将产生实例的方法与客户分离,降低耦合度。
使用原型模式,可以同时使用单例模式产生工厂实例,使用抽象工厂管理生产线,再使用原型模式返回对象。
核心实现:
实现拷贝构造函数。
适用场景:
实例非常复杂,设置非常麻烦,实例的初始化非常耗时;
试图获得实例的客户并没有设置参数的能力,或者不想花费时间去设置参数。
模型类图:
图 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 请按任意键继续. . .