上篇文章,介绍了《大话设计模式》的第8章——工厂方法。

本篇,来介绍《大话设计模式》的第9章——原型模式。并通过C++代码实现实例代码的功能。

1 原型模式

原型模式(Prototype):用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

原型模式的类图如下:

  • Client:让一个原型克隆自身从而创建一个新的对象
  • Prototype:原型类,声明一个克隆自身的接口
  • ConcreatePrototype:具体原型类,实现一个克隆自身的操作

大话设计模式解读06-原型模式_原型模式

2 实例

背景:书中小故事,小菜要去找工作,准备了很厚一叠简历,大鸟在感慨他那时找工作的时候,都是手写简历,现在印简历就像印草纸一样,由此联想到代码中的复制粘贴。

题目:用代码的形式来实现简历类的功能,要求必须要有姓名,可以设置性别和年龄,可以设置工作经历,最终需要写三份简历。

大话设计模式解读06-原型模式_实例化_02

2.1 版本一:单一简历类

版本一的实现比较简单,仅设计一个简历类。

大话设计模式解读06-原型模式_实例化_03

2.1.1 简历类

简历类的实现如下:

  • 简历类构造时传入姓名作为参数
  • 调用设置个人信息方法SetPersonalInfo,可设置性别和年龄
  • 调用设置工作经历方法SetWorkExperience,可设置工作时间与工作的公司
  • 调用显示方法Display,可显示简历的完整内容
// 简历类
class Resume
{
public:
    Resume(std::string name)
    {
        m_name = name;
    }
    
    // 设置个人信息
    void SetPersonalInfo(std::string sex, int age)
    {
        m_sex  = sex;
        m_iAge = age;
    }    
    
    // 设置工作经历
    void SetWorkExperience(std::string timeArea, std::string company)
    {
        m_timeArea = timeArea;
        m_company  = company;
    }  
    
    // 显示
    void Display()
    {
        printf("%s %s %d\n", m_name.c_str(), m_sex.c_str(), m_iAge);
        printf("工作经历: %s %s\n", m_timeArea.c_str(), m_company.c_str());
    }

private:
    std::string m_name;
    std::string m_sex;
    int         m_iAge;
    std::string m_timeArea;
    std::string m_company;
};
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
2.1.2 主函数

首先,实例化三份简历,并分别设置具体的内容,

然后,就可以调用展示接口来显示出来了。

int main()
{
    // 实例化三份简历
    Resume a = Resume("大鸟");
    a.SetPersonalInfo("男", 29);
    a.SetWorkExperience("2018-2020", "XX公司");
    
    Resume b = Resume("大鸟");
    b.SetPersonalInfo("男", 29);
    b.SetWorkExperience("2018-2020", "XX公司");
    
    Resume c = Resume("大鸟");
    c.SetPersonalInfo("男", 29);
    c.SetWorkExperience("2018-2020", "XX公司");
    
    // 显示出来
    a.Display();
    b.Display();
    c.Display();
    
    return 0;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.

代码运行效果如下:

大话设计模式解读06-原型模式_原型模式_04

下面来看版本二。

2.2 版本二:原型模式

版本二,使用原型模型,设计了一个简历原型类,定义了简历所具有的基本信息及接口,简历类则继承简历原型类,

另外,工作经历设计为一个单独类,通过组合方式,作为简历类的一部分。

大话设计模式解读06-原型模式_实例化_05

注1:书中代码使用的C#,C#中有MemberwiseClone方法进行拷贝,而C++中无此方法,因此需要重新实现一个Clone方法。

注2:对对象进行拷贝时,会涉及到浅拷贝与深拷贝的概念,而MemberwiseClone方法对于引用类型的对象,复制的是引用而不是复制对象,这样就会带来修改了复制后的对象,原对象也会被修改。因此,书中例子讲工作经历单独拎出来作为一个类,拷贝简历时,简历中的工作经历将为引用类型,以此来对比演示浅拷贝带来的影响。解决方法就是改为深拷贝,在工作经历类的内部也实现一个Clone方法。

2.2.1 工作经历类、简历原型类与简历类

这里需要实现三个类:

  • 工作经历类:作为简历的一部分,实现工作时间与对应的工作公司的信息,并提供克隆接口
  • 简历原型类:一个抽象类,定义了简历所具有的基本信息及接口,并提供克隆接口,实现简历克隆
  • 简历类:继承简历原型类,并实现简历原型中定义的方法
// 工作经历
class WorkExperience
{
public:
    WorkExperience() {}
    ~WorkExperience(){}
    
    WorkExperience(WorkExperience *work)
    {
        this->m_timeArea = work->m_timeArea;
        this->m_company  = work->m_company;
    }
    
    // 设置/获取工作时间
    void SetWorkTiemArea(std::string timeArea)
    {
        m_timeArea = timeArea;
    }  
    
    std::string GetWorkTiemArea()
    {
        return m_timeArea;
    }
    
    // 设置/获取工作公司
    void SetWorkCompany(std::string company)
    {
        m_company  = company;
    }
    
    std::string GetWorkCompany()
    {
        return m_company;
    } 
    
    // Clone
    WorkExperience *Clone()
    {
        return new WorkExperience(this);
    } 
    
private:
    std::string m_timeArea;
    std::string m_company;
};

// 简历原型类
class Prototype
{
public:
    virtual void SetPersonalInfo(std::string sex, int age) = 0;
    virtual void Display() = 0;
    virtual Prototype *Clone() = 0;
    
protected:
    std::string m_name;
    std::string m_sex;
    int         m_iAge;    
    std::string m_timeArea;
    std::string m_company;
};

// 简历类
class Resume : public Prototype
{
public:
    Resume(std::string name)
    {
        m_name  = name;
        m_pWork = new WorkExperience();
    }
    
    ~Resume()
    {
        if (m_pWork)
        {
            delete m_pWork;
        }
    }
    
    // 设置个人信息
    void SetPersonalInfo(std::string sex, int age)
    {
        m_sex  = sex;
        m_iAge = age;
    }   
     
    // 设置工作经历
    void SetWorkExperience(std::string timeArea, std::string company)
    {
        m_pWork->SetWorkTiemArea(timeArea);
        m_pWork->SetWorkCompany(company);
    }  
    
    // 显示
    void Display()
    {
        printf("%s %s %d\n", m_name.c_str(), m_sex.c_str(), m_iAge);
        printf("工作经历: %s %s\n", m_pWork->GetWorkTiemArea().c_str(), m_pWork->GetWorkCompany().c_str());
    }
    
    Resume *Clone()
    {
        Resume *pClone = new Resume(m_name);
        pClone->SetPersonalInfo(m_sex, m_iAge);
        return pClone;
    }
    
private:
    WorkExperience *m_pWork;
};
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
2.2.2 主函数

首先,实例化了一个简历A,并设置一些基础信息。

然后,通过克隆的方式,产生简历B与简历C,并对简历B与简历C进行个性化修改。

最后,调用显示接口显示简历的内容。

int main()
{
    // 实例化三份简历
    Resume a = Resume("大鸟");
    a.SetPersonalInfo("男", 29);
    a.SetWorkExperience("2018-2020", "XX公司");
    
    Resume *b = a.Clone();
    b->SetWorkExperience("2018-2021", "YY公司");
    
    Resume *c = a.Clone();
    c->SetPersonalInfo("男", 24);
    c->SetWorkExperience("2018-2022", "ZZ公司");
    
    // 显示出来
    a.Display();
    b->Display();
    c->Display();
    
    delete b;
    delete c;
      
    return 0;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.

代码运行效果如下:

大话设计模式解读06-原型模式_实例化_06

原型模式的主要特点在于设计了一个克隆方法,通过克隆,将当前对象的当前状态做一个拷贝,形成一个新的对象,注意体会这里的“当前状态”,它不是初始状态,而是在程序运行到某一时刻时其内部属性的最新状态。

总结

本篇介绍了设计模式中的原型模式,并通过简历编写的实例,使用C++编程,来演示原型模式的使用。