1.原型模式的动机
为什么会出现原型模式?原型模式是为了解决什么问题?本篇文章将一探究竟,深入学习一下什么是原型模式。在软件系统中,经常面临着某些结构复杂对象的创建工作。由于需求的变化,这些对象经常面临着剧烈的变化,但是它们却拥有比较稳定一致的接口。
如何应对上面的这种变化?如何向客户端程序(使用这些对象的程序)隔离出这些易变对象,从而使得依赖这些易变对象的客户端程序不会随着需求改变而变化?
2.原型模式的定义
定义:使用原型实例指定待创建对象的种类,然后通过拷贝(深拷贝),[关于什么是浅拷贝与深拷贝,可参见参考资料解释]。这些原型来创建新的对象。具体来说:将一个原型对象传递给要发动创建的对象(即客户端对象),客户端对象通过请求原型对象复制自己来实现创建新对象的过程。
3.原型模式的UML类图
原型模式的UML类图中主要有三个角色:
(1).抽象原型类(AbstractPrototype):声明复制自身的接口;
(2).具体原型类(ConcretePrototype):实现复制自身的接口;
(3).客户端:声明一个抽象原型类,根据客户的具体需求复制具体原型类对象实例;
4.原型模式实战
相信小伙伴们的读书生涯多少都会遇到过“抄作业”这件事,下面假设下面这种场景:2021年寒假就快结束了,但是CurryCoder同学的寒假作业还没写完。着急之下,他想到可以借用同学小王的作业来“参考参考”。于是有了下面的HomeWorkModel类。
// 作业类 class HomeWorkModel{ public: char* modelName; void setHomeWorkModelName(char* iName) { this->modelName = iName; } };
接着,定义原型类和copy()方法,具体如下所示:
// 抽象原型类 class AbstractPrototypeHomeWork{ public: AbstractPrototypeHomeWork(){} virtual AbstractPrototypeHomeWork* copy() = 0; }; // 具体原型类 class ConcretePrototypeHomeWork:public AbstractPrototypeHomeWork{ public: ConcretePrototypeHomeWork(){} ConcretePrototypeHomeWork(char* iName, int iIdNum, char* modelName){ this->name = iName; this->idNum = iIdNum; this->homeworkModel = new HomeWorkModel(); this->homeworkModel->setHomeWorkModelName(modelName); } ConcretePrototypeHomeWork* copy(){ ConcretePrototypeHomeWork* homework = new ConcretePrototypeHomeWork(); homework->setName(this->name); homework->setIdNum(this->idNum); homework->homeworkModel = this->homeworkModel; return homework; } void setName(char* iName) { this->name = iName; } void setIdNum(int iIdNum) { this->idNum = iIdNum; } void setModel(HomeWorkModel* iHomeWorkModel) { this->homeworkModel = iHomeWorkModel; } // 打印信息 void printPersonInfo() { cout << "\tname: " << this->name << endl; cout << "\tidNum: " << this->idNum << endl; cout << "\tmodelName: " << this->homeworkModel->modelName << endl; } private: char* name; int idNum; HomeWorkModel* homeworkModel; };
最后,利用浅拷贝在客户端程序中实现“借鉴借鉴”小王作业的效果。
// 浅拷贝 int main() { ConcretePrototypeHomeWork* xiaowang = new ConcretePrototypeHomeWork("xiaowang", 10086, "xiaowang_Model"); cout << "小王的作业:\n"; xiaowang->printPersonInfo(); cout << "\nCurryCoder的直接抄小王的作业......\n"; ConcretePrototypeHomeWork* CurryCoder = xiaowang; cout << "\nCurryCoder的作业:\n"; CurryCoder->printPersonInfo(); cout << endl; cout << "CurryCoder抄完作业后,必须修改名字和学号。否则,老师会发现的.\n"; CurryCoder->setName("CurryCoder"); CurryCoder->setIdNum(10010); HomeWorkModel* CurryCoderModel = new HomeWorkModel(); CurryCoderModel->setHomeWorkModelName("CurryCoder_Model"); CurryCoder->setModel(CurryCoderModel); cout << endl; cout << "小王的作业:\n"; xiaowang->printPersonInfo(); cout << "CurryCoder的作业:\n"; CurryCoder->printPersonInfo(); return 0; }
通过上面浅拷贝的操作,我们可以发现当CurryCoder将名字和学号修改成自己的时,原先小王作业本中的名字和学号也会被修改。因此,必须使用深拷贝操作即(使用原型模式)实现拷贝小王作业的目的,客户端程序如下所示:
// 深拷贝 int main() { ConcretePrototypeHomeWork* xiaowang = new ConcretePrototypeHomeWork("xiaowang", 10086, "xiaowang_Model"); cout << "小王的作业:\n"; ConcretePrototypeHomeWork* CurryCoder = xiaowang->copy(); cout << "\nCurryCoder的作业:\n"; cout << endl; cout << "CurryCoder抄完作业后,必须修改名字和学号。否则,老师会发现的.\n"; CurryCoder->setName("CurryCoder"); CurryCoder->setIdNum(10010); HomeWorkModel* CurryCoderModel = new HomeWorkModel(); CurryCoderModel->setHomeWorkModelName("CurryCoder_Model"); CurryCoder->setModel(CurryCoderModel); cout << endl; cout << "小王的作业:\n"; xiaowang->printPersonInfo(); cout << "CurryCoder的作业:\n"; CurryCoder->printPersonInfo(); return 0; }
通过上面深拷贝的操作,我们可以发现当CurryCoder将名字和学号修改成自己的时,原先小王作业本中的名字和学号也不会被修改。
5.原型模式总结
(1).原型模式同样用于隔离类对象的使用者和具体类型(易变类)之间的耦合关系,它同样要求这些易变类拥有稳定的接口。
(2).原型模式对于如何创建易变类的实体对象采用原型深拷贝的方法来做,它使得我们可以非常灵活地动态创建拥有某些稳定接口的新对象——所需工作仅仅是注册一个新类的对象(即原型),然后在任何需要的地方copy即可。
(3).原型模式中copy()方法可以利用某些框架中的序列化来实现深拷贝。
6.参考资料
[1]浅拷贝与深拷贝 https://www.jianshu.com/p/afc71c3681da