设计模式之原型模式

设计模式之原型模式

  • 前言
  • 原型模式
    • 基本实现
    • 深拷贝与浅拷贝

前言

在开发中,有时候可能对同一种类型要实例化多个对象,类型不改变但是类中的数据发生改变,如果每次都是调用new的话,都会执行一次构造函数,导致效率十分底下,这时候我们考虑原型模式。

原型模式

基本实现

原型模式就是用原型实例创建对象的种类,并且通过拷贝这些原型创建新的对象。原型模式其实就是从一个对象再创建另外一个可定制的对象,而且不需要知道任何创建的细节。

简单点来说,原型模式就是实例自己去拷贝自己。

下面来看下原型模式的示例代码

#include 
#include 

class iCloneable {
public:
    iCloneable() = default;
    ~iCloneable() = default;


    virtual iCloneable* clone() = 0;
    virtual void setResume(int age, const std::string& name) = 0;
    virtual void display() = 0;
};

class Resume:public iCloneable {
public:
    Resume(int age, const char* name):m_age(age), m_name(name){}
    ~Resume(){}

    Resume(Resume&&) = delete;
    Resume& operator=(const Resume&)=delete;
    Resume& operator=(Resume&&) = delete;

    virtual iCloneable* clone() override{
        return new Resume(*this);
    }
    virtual void setResume(int age, const std::string& name)override {
        m_age = age;
        m_name = name;
    }

    virtual void display()override {
        printf("age=%d, name=%s\n", m_age, m_name.c_str());
    }
    
private:
    Resume(const Resume&) =default;

    int m_age;
    std::string m_name;
};


int main(int argc, char** argv)
{
    Resume* rsm = new Resume(21, "john");
    rsm->display();
    iCloneable * r1 = rsm->clone();
    r1->display();
    r1->setResume(22, "Tom");
    r1->display();
    iCloneable*r2 = r1->clone();
    return 0;
}

示例中,iCloneable是一个基类,提供了三个接口,clone,setResumedisplay,分别用来克隆自身,初始化数据以及显示数据,子类Resume继承iCloneable,重写这三个接口,并隐藏自身的拷贝构造函数,这样,使用者只需要调用clone接口就可以获得一个自身的拷贝,大大提高了效率。然而当前这种实现方式,如果类成员是动态申请的资源的话,就会出现一些问题,你看出来了吗?

深拷贝与浅拷贝

在进行类的拷贝构造时,当前的方式是直接将所有的类成员复制一份,表现出的现象就是上述代码中的r1rsm的age和name在被重新赋值前是相同的,此时,如果类成员是动态资源,会出现什么现象呢,观察下述代码。

#include 
#include 

class iCloneable {
public:
    iCloneable() = default;
    ~iCloneable() = default;


    virtual iCloneable* clone() = 0;
    virtual void setResume(int age, const std::string& name) = 0;
    virtual void display() = 0;
};
class WorkExperience{
public:
    WorkExperience(int year):m_workYear(year){}
    ~WorkExperience(){}

    int getWorkYear(){return m_workYear;}

    int m_workYear{5};
};
class Resume:public iCloneable {
public:
    Resume(int age, const char* name):m_age(age), m_name(name){
        m_workExperience = new WorkExperience(9);
    }
    ~Resume(){}

    Resume(Resume&&) = delete;
    Resume& operator=(const Resume&)=delete;
    Resume& operator=(Resume&&) = delete;

    virtual iCloneable* clone() override{
        return new Resume(*this);
    }
    virtual void setResume(int age, const std::string& name)override {
        m_age = age;
        m_name = name;
    }

    void setWorkYear(int year){ m_workExperience->m_workYear = year;}
    virtual void display()override {
        printf("age=%d, name=%s\n", m_age, m_name.c_str());
        printf("work year:%d\n", m_workExperience->m_workYear);
    }

    
private:
    Resume(const Resume&) =default;

    int m_age;
    std::string m_name;
    WorkExperience* m_workExperience;
};


int main(int argc, char** argv)
{
    Resume* rsm = new Resume(21, "john");
    rsm->display();
    iCloneable * r1 = rsm->clone();
    r1->display();
    r1->setResume(22, "Tom");
    r1->display();

    rsm->setWorkYear(17);
    r1->display();    
    iCloneable*r2 = r1->clone();
    return 0;
}

输出

age=21, name=john
work year:9
age=21, name=john
work year:9
age=22, name=Tom
work year:9
age=22, name=Tom
work year:17

可以看到,当rsm修改了workYear时,r1的workYear也发生了变化,这就是浅拷贝。

浅拷贝就是拷贝了指针,但是没有拷贝指针指向的内容,导致两个指针指向了相同的一块内容,修改其中任意一个,都会引起另一个也发生变化。

浅拷贝的解决方案就是深拷贝,我们把指针指向的内容也复制一份,就不会出现这个问题了。

#include 
#include 

class iCloneable {
public:
    iCloneable() = default;
    ~iCloneable() = default;


    virtual iCloneable* clone() = 0;
    virtual void setResume(int age, const std::string& name) = 0;
    virtual void display() = 0;
};
class WorkExperience{
public:
    WorkExperience(int year):m_workYear(year){}
    ~WorkExperience(){}

    int getWorkYear(){return m_workYear;}

    int m_workYear{5};
};
class Resume:public iCloneable {
public:
    Resume(int age, const char* name):m_age(age), m_name(name){
        m_workExperience = new WorkExperience(9);
    }
    ~Resume(){}

    Resume(Resume&&) = delete;
    Resume& operator=(const Resume&)=delete;
    Resume& operator=(Resume&&) = delete;

    virtual iCloneable* clone() override{
        return new Resume(*this);
    }
    virtual void setResume(int age, const std::string& name)override {
        m_age = age;
        m_name = name;
    }

    void setWorkYear(int year){ m_workExperience->m_workYear = year;}
    virtual void display()override {
        printf("age=%d, name=%s\n", m_age, m_name.c_str());
        printf("work year:%d\n", m_workExperience->m_workYear);
    }

    
private:
    Resume(const Resume& rsm) {
        this->m_age = rsm.m_age;
        this->m_name = rsm.m_name;
        this->m_workExperience = new WorkExperience(*rsm.m_workExperience);
    };

    int m_age;
    std::string m_name;
    WorkExperience* m_workExperience;
};


int main(int argc, char** argv)
{
    Resume* rsm = new Resume(21, "john");
    rsm->display();
    iCloneable * r1 = rsm->clone();
    r1->display();
    r1->setResume(22, "Tom");
    r1->display();

    rsm->setWorkYear(17);
    r1->display();    
    iCloneable*r2 = r1->clone();
    return 0;
}

控制台输出

age=21, name=john
work year:9
age=21, name=john
work year:9
age=22, name=Tom
work year:9
age=22, name=Tom
work year:9

可以看到,即便是改变了rsm的workYear, r1的workYear还是9。
以上图片来源于程杰的《大话设计模式》,需要的同学可以关注公众号程序员DeRozan,回复1207领取。

《C++ Primer》《Effective C++》是C++开发者必不可少的书籍,如果你想入门C++,以及想要精进C++开发技术,这两本书可以说必须要有。此外,《Linux高性能服务器编程》以及《Linux多线程服务端编程:使用muduo C++网络库》.(陈硕)》是快速提高你的linux开发能力的秘籍。《大话设计模式》可以增强我们的模型提取及设计能力,写出更优雅的代码。同时,《操作系统导论》更是开发必读书目,在网上搜索相关资源也要花费一些力气,需要的同学可以关注公众号【程序员DeRozan】,回复【1207】快速免费领取~

你可能感兴趣的:(设计模式,设计模式,原型模式)