文章目錄
- 原型模式簡介
- 原型模式結構
- 關于克隆方法:淺拷貝/深拷貝
- 原型模式代碼實例
- 定義原型類和克隆方法
- 客戶端使用代碼示例
- 示例一:淺拷貝
- 示例二:深拷貝
- 原型模式總結
- 開閉原則
代碼倉庫
原型模式:用原型實例指定創建對象的種類,并且通過拷貝這些原型創建新的對象。
定義看起來有點繞口,不妨簡單的理解為:原型模式就是用來克隆對象的。
舉個例子,比如有一天,周杰倫到奶茶店點了一份不加冰的原味奶茶,你說我是周杰倫的忠實粉,我也要一份跟周杰倫一樣的。
即便Jungle讀書少,Jungle也清晰地記得中學生物課本上提到過的克隆羊“多利”。雖然多利壽命不長,但它的出現對“克隆(Clone)”技術意義重大。克隆,直觀說就是從原有生物體上取體細胞,然后無性繁殖出有完全相同基因的個體或種群。這么說來中國的克隆技術其實是世界領先的,因為孫悟空拔一根毫毛變出許多一模一樣的孫悟空的傳說本質上就是克隆!而本文將要介紹的原型模式,將克隆技術應用到了軟件設計層面。
原型模式簡介
原型模式通過復制一個已有對象來獲取更多相同或者相似的對象。原型模式定義如下:
使用原型實例指定待創建對象的類型,并且通過復制這個原型來創建新的對象。
原型模式的工作原理是將一個原型對象傳給要發動創建的對象(即客戶端對象),這個要發動創建的對象通過請求原型對象復制自己來實現創建過程。從工廠方法角度而言,創建新對象的工廠就是原型類自己。軟件系統中有些對象的創建過程比較復雜,且有時需要頻繁創建,原型模式通過給出一個原型對象來指明所要創建的對象的類型,然后用復制這個原型對象的辦法創建出更多同類型的對象,這就是原型模式的意圖所在。
原型模式結構
關于克隆方法:淺拷貝/深拷貝
原型模式代碼實例
明天就是周一了,Jungle又陷入了苦惱中,因為作業還沒完成。于是Jungle想拿著哥哥Single的作業來抄一份。雖然抄襲作業并不好,但是邊抄邊學借鑒一下也是可以的。于是乎,Jungle開始動起手來……
作業包含幾個部分:姓名(name)、學號(idNum)、模型(workModel)。首先定義一個workModel類:
// work model類
// 作為復雜的成員對象,供ConcreteWork引用。
class WorkModel {
public:std::string modelName;WorkModel() : modelName("") {}WorkModel(const std::string& iName) : modelName(iName) {}void setWorkModelName(const std::string& iName) {this->modelName = iName;}std::string getWorkModelName() const {return modelName;}// 深拷貝構造函數// 深拷貝構造函數防止多個對象共享同一個內存(避免淺拷貝問題)。// std::string 本身已經重載了拷貝構造函數和賦值運算符,實現了深拷貝的語義。WorkModel(const WorkModel& other) : modelName(other.modelName) {}
};
該實例UML圖如下:
定義原型類和克隆方法
// 抽象原型類PrototypeWork
// 定義抽象接口clone(),具體原型類必須實現該方法。
class PrototypeWork {
public:PrototypeWork() {}virtual ~PrototypeWork() {}virtual PrototypeWork* clone() = 0;virtual void printWorkInfo() const = 0;
};// 具體原型類PrototypeWork
class ConcreteWork: public PrototypeWork {
public:ConcreteWork(const string& iName, int iIdNum, const string& modelName): name(iName), idNum(iIdNum), workModel(new WorkModel(modelName)) {}// 深拷貝構造函數ConcreteWork(const ConcreteWork& other): name(other.name), idNum(other.idNum), workModel(new WorkModel(*other.workModel)) {}// 深拷貝賦值運算符// ConcreteWork& operator=(const ConcreteWork& other) {// if (this != &other) {// name = other.name;// idNum = other.idNum;// delete workModel;// workModel = new WorkModel(*other.workModel);// }// return *this;// }// 克隆接口實現(返回深拷貝)PrototypeWork* clone() override {return new ConcreteWork(*this);}~ConcreteWork() {delete workModel;}// 打印work信息void printWorkInfo() const override {std::cout << "Name: " << name << std::endl;std::cout << "IdNum: " << idNum << std::endl;std::cout << "ModelName: " << workModel->getWorkModelName() << std::endl;}// 新增set方法void setName(const std::string& newName) {name = newName;}void setIdNum(int newIdNum) {idNum = newIdNum;}void setModel(WorkModel* newModel) {if (workModel != nullptr) {delete workModel;}workModel = newModel;}// 新增get方法std::string getName() const {return name;}int getIdNum() const {return idNum;}WorkModel* getModel() const {return workModel;}private:string name;int idNum;WorkModel* workModel;
};
客戶端使用代碼示例
示例一:淺拷貝
#include "PrototypePattern.h"int main() {
// #if 0
// // 下面的代碼將不會被編譯,也不會執行ConcreteWork *singleWork = new ConcreteWork("Single",1001,"Single_Model");printf("\nSingle的作業:\n");singleWork->printWorkInfo();printf("\njungle直接抄作業……\n");ConcreteWork *jungleWork = singleWork;printf("\nJungle的作業:\n");jungleWork->printWorkInfo();// 抄完改名字和學號,否則會被老師查出來printf("\njungle抄完改名字和學號,否則會被老師查出來……\n");jungleWork->setName("jungle");jungleWork->setIdNum(1002);WorkModel *jungleModel = new WorkModel();jungleModel->setWorkModelName("Jungle_Model");jungleWork->setModel(jungleModel);// 檢查下是否改對了printf("\nSingle的作業:\n");singleWork->printWorkInfo();printf("\nJungle的作業:\n");jungleWork->printWorkInfo();
// #endif
#if 0
// 下面的代碼將不會被編譯,也不會執行ConcreteWork *singleWork = new ConcreteWork("Single", 1001, "Single_Model");printf("\nSingle的作業:\n");// clone() 返回 PrototypeWork*,需類型轉換ConcreteWork* jungleWork = dynamic_cast<ConcreteWork*>(singleWork->clone());printf("\njungle直接抄作業……\n");// 抄完改名字和學號,否則會被老師查出來printf("\njungle抄完改名字和學號,否則會被老師查出來……\n");jungleWork->setName("jungle");jungleWork->setIdNum(1002);WorkModel *jungleModel = new WorkModel();jungleModel->setWorkModelName("Jungle_Model");jungleWork->setModel(jungleModel);// 檢查下是否改對了printf("\nSingle的作業:\n");singleWork->printWorkInfo();printf("\nJungle的作業:\n");jungleWork->printWorkInfo();delete singleWork;delete jungleWork;
#endif return 0;
}
顯然,這不是我們想要的結果。接下來我們使用clone方法。
示例二:深拷貝
```cpp
#include "PrototypePattern.h"int main() {
#if 0
// 下面的代碼將不會被編譯,也不會執行ConcreteWork *singleWork = new ConcreteWork("Single",1001,"Single_Model");printf("\nSingle的作業:\n");singleWork->printWorkInfo();printf("\njungle直接抄作業……\n");ConcreteWork *jungleWork = singleWork;printf("\nJungle的作業:\n");jungleWork->printWorkInfo();// 抄完改名字和學號,否則會被老師查出來printf("\njungle抄完改名字和學號,否則會被老師查出來……\n");jungleWork->setName("jungle");jungleWork->setIdNum(1002);WorkModel *jungleModel = new WorkModel();jungleModel->setWorkModelName("Jungle_Model");jungleWork->setModel(jungleModel);// 檢查下是否改對了printf("\nSingle的作業:\n");singleWork->printWorkInfo();printf("\nJungle的作業:\n");jungleWork->printWorkInfo();
#endif
// #if 0
// 下面的代碼將不會被編譯,也不會執行ConcreteWork *singleWork = new ConcreteWork("Single", 1001, "Single_Model");printf("\nSingle的作業:\n");singleWork->printWorkInfo();// clone() 返回 PrototypeWork*,需類型轉換printf("\njungle直接抄作業……\n");ConcreteWork* jungleWork = dynamic_cast<ConcreteWork*>(singleWork->clone());printf("\nJungle的作業:\n");jungleWork->printWorkInfo();// 抄完改名字和學號,否則會被老師查出來printf("\njungle抄完改名字和學號,否則會被老師查出來……\n");jungleWork->setName("jungle");jungleWork->setIdNum(1002);WorkModel *jungleModel = new WorkModel();jungleModel->setWorkModelName("Jungle_Model");jungleWork->setModel(jungleModel);// 檢查下是否改對了printf("\nSingle的作業:\n");singleWork->printWorkInfo();printf("\nJungle的作業:\n");jungleWork->printWorkInfo();delete singleWork;delete jungleWork;
// #endif return 0;
}
原型模式總結
開閉原則
之后我會持續更新,如果喜歡我的文章,請記得一鍵三連哦,點贊關注收藏,你的每一個贊每一份關注每一次收藏都將是我前進路上的無限動力 !!!↖(▔▽▔)↗感謝支持!