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


    原型模式:

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

    背景补充:

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

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

    核心实现:

    实现拷贝构造函数。

    适用场景:

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

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

    模型类图:

            

    图 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
    请按任意键继续. . .
  • 相关阅读:
    6、short s1 = 1; s1 = s1 + 1;有错吗?short s1 = 1; s1 += 1;有错吗?-Java面试题答案
    5、switch语句能否作用在byte上,能否作用在long上,能否作用在String上?-Java面试题答案
    4、在JAVA中如何跳出当前的多重嵌套循环?-Java面试题答案
    3、说说&和&&的区别-Java面试题答案
    2、Java有没有goto?-Java,面试题答案
    1、一个".java"源文件中是否可以包括多个类(不是内部类)?有什么限制?-Java面试题答案
    javaScript属性
    javaScript基本知识
    javaScript额外笔记
    OOP-基础知识(c#)
  • 原文地址:https://www.cnblogs.com/zanzan101/p/3406334.html
Copyright © 2020-2023  润新知