原型模式:通过一个对象(原型对象)克隆出多个一模一样的对象。

  • 克隆对象自身实际上是需要调用类的拷贝构造函数。
  • 克隆对象意味着复制出一个全新的对象,所以设计到深浅拷贝时都要实现深拷贝

    一、通过工厂方法模式演变到原型模式

    ```cpp

    pragma once

/ 原型模式:通过一个对象(原型模式)克隆出多个一模一样的对象 通过工厂模式演变成原型模式 /

include

using namespace std;

class Monster { public: // 构造函数 Monster(int life, int magic, int attack) : m_life(life) , m_magic(magic) , m_attack(attack) { } // 做父类时析构函数应该为虚函数 virtual ~Monster() { }

  1. // 具体的实现在子类中进行
  2. virtual Monster* clone() = 0;

protected: int m_life; // 生命值 int m_magic; // 魔法值 int m_attack; // 攻击力 };

// 亡灵类怪物 class Undead : public Monster { public: // 构造函数 Undead(int life, int magic, int attack) : Monster(life, magic, attack) { cout << “A undead monster came into the world.” << endl; } // 拷贝构造函数 Undead(const Undead& el) : Monster(el) { // 初始化类别中注意对父类子对象的初始化 cout << “Calling the copy constructor creates an undead class monster.” << endl; } Monster clone() override { //return new Undead(300, 80, 80); // 创建亡灵类怪物 return new Undead(this); // 触发拷贝构造函数的调用来创建亡灵类怪物 } // …其他代码略 };

// 元素类怪物 class Element : public Monster { public: // 构造函数 Element(int life, int magic, int attack) : Monster(life, magic, attack) { cout << “A element monster came into the world.” << endl; } // 拷贝构造函数 Element(const Element& el) : Monster(el) { // 初始化类别中注意对父类子对象的初始化 cout << “Calling the copy constructor creates an element class monster.” << endl; } Monster clone() override { return new Element(this); } // …其他代码略 };

// 机械类怪物 class Mechanic : public Monster { public: // 构造函数 Mechanic(int life, int magic, int attack) : Monster(life, magic, attack) { cout << “A mechanic monster came into the world.” << endl; } // 拷贝构造函数 Mechanic(const Mechanic& el) : Monster(el) { // 初始化类别中注意对父类子对象的初始化 cout << “Calling the copy constructor creates an mechanic class monster.” << endl; } Monster clone() override { return new Mechanic(this); } // …其他代码略 };

void protytype1() { // 创建一只机械类怪物对象作为原型对象以用于克隆目的 Mechanic mechanic(400, 0, 110); // 创建一只元素类怪物对象作为原型对象以用于克隆目的 Monster* element = new Element(200, 80, 100); // 可以直接new,也可以通过工厂模式来创建原型对象

  1. Monster* clone1 = mechanic.clone(); // 使用原型对象克隆出新的机械类怪物对象
  2. Monster* clone2 = element->clone(); // 使用原型对象克隆出新的元素类怪物对象
  3. // 可以对clone1 clone2所指向的对象进行各种操作(实现具体的业务逻辑)
  4. // ...
  5. // 释放资源(克隆出来的怪物对象、堆中创建的对象)
  6. delete clone1;
  7. delete clone2;
  8. delete element;

}

  1. <a name="arf9w"></a>
  2. # 二、引入原型模式
  3. <a name="zdCDN"></a>
  4. ## 2.1 定义
  5. 定义:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。简单来说就是通过克隆来创建新的对象实例。
  6. <a name="GeWCR"></a>
  7. ## 2.2 示例代码
  8. ```cpp
  9. #pragma once
  10. #include <iostream>
  11. using namespace std;
  12. class Monster {
  13. public:
  14. // 构造函数
  15. Monster(int life, int magic, int attack)
  16. : m_life(life)
  17. , m_magic(magic)
  18. , m_attack(attack)
  19. {
  20. }
  21. // 做父类时析构函数应该为虚函数
  22. virtual ~Monster() { }
  23. // 具体的实现在子类中进行
  24. virtual Monster* clone() = 0;
  25. protected:
  26. int m_life; // 生命值
  27. int m_magic; // 魔法值
  28. int m_attack; // 攻击力
  29. };
  30. // 亡灵类怪物
  31. class Undead : public Monster {
  32. public:
  33. // 构造函数
  34. Undead(int life, int magic, int attack)
  35. : Monster(life, magic, attack)
  36. {
  37. cout << "A undead monster came into the world." << endl;
  38. }
  39. // 拷贝构造函数
  40. Undead(const Undead& el)
  41. : Monster(el)
  42. { // 初始化类别中注意对父类子对象的初始化
  43. cout << "Calling the copy constructor creates an undead class monster." << endl;
  44. }
  45. Monster* clone() override
  46. {
  47. //return new Undead(300, 80, 80); // 创建亡灵类怪物
  48. return new Undead(*this); // 触发拷贝构造函数的调用来创建亡灵类怪物
  49. }
  50. // ...其他代码略
  51. };
  52. // 元素类怪物
  53. class Element : public Monster {
  54. public:
  55. // 构造函数
  56. Element(int life, int magic, int attack)
  57. : Monster(life, magic, attack)
  58. {
  59. cout << "A element monster came into the world." << endl;
  60. }
  61. // 拷贝构造函数
  62. Element(const Element& el)
  63. : Monster(el)
  64. { // 初始化类别中注意对父类子对象的初始化
  65. cout << "Calling the copy constructor creates an element class monster." << endl;
  66. }
  67. Monster* clone() override
  68. {
  69. return new Element(*this);
  70. }
  71. // ...其他代码略
  72. };
  73. // 机械类怪物
  74. class Mechanic : public Monster {
  75. public:
  76. // 构造函数
  77. Mechanic(int life, int magic, int attack)
  78. : Monster(life, magic, attack)
  79. {
  80. cout << "A mechanic monster came into the world." << endl;
  81. }
  82. // 拷贝构造函数
  83. Mechanic(const Mechanic& el)
  84. : Monster(el)
  85. { // 初始化类别中注意对父类子对象的初始化
  86. cout << "Calling the copy constructor creates an mechanic class monster." << endl;
  87. }
  88. Monster* clone() override
  89. {
  90. return new Mechanic(*this);
  91. }
  92. // ...其他代码略
  93. };
  94. // 没有克隆方法时需要类型转换进行判断来创建对象
  95. void globalCreateMonster(Monster* monster)
  96. {
  97. Monster* tempMonster = nullptr;
  98. if (dynamic_cast<Undead*>(monster) != nullptr) {
  99. tempMonster = new Undead(300, 50, 80);
  100. } else if (dynamic_cast<Element*>(monster) != nullptr) {
  101. tempMonster = new Element(200, 80, 100);
  102. } else if (dynamic_cast<Mechanic*>(monster) != nullptr) {
  103. tempMonster = new Mechanic(200, 80, 100);
  104. }
  105. if (tempMonster != nullptr) {
  106. // 实现各种业务逻辑
  107. // ....
  108. // 不要忘记释放资源
  109. delete tempMonster;
  110. }
  111. }
  112. // 有克隆方法时
  113. void globalCreateMonster2(Monster* monster)
  114. {
  115. // 根据已有对象直接创建新对象,不需要已有对象所属的类型
  116. Monster* tempMonster = monster->clone();
  117. // 实现各种业务逻辑
  118. // ....
  119. // 不要忘记释放资源
  120. delete tempMonster;
  121. }
  122. void protytype2()
  123. {
  124. // 创建一只元素类怪物
  125. Monster* element = new Element(200, 80, 100);
  126. globalCreateMonster2(element);
  127. delete element;
  128. }

2.3 类图

原型模式.svg
由类图可看出,原型模式的两种角色:

  • Prototype(抽象原型类):Monster类
  • ConcretePrototype(具体原型类):Undead、Element、Mechanic类

如果对象内部数据比较复杂多变并且在创建对象的时候希望保持对象的当前状态,那么用原型模式显然比用工厂方法模式更合适。

2.4 工厂模式与原型模式的异同点

  • 都不需要程序员知道所创建对象所属的类名。
  • 工厂方法模式中的createMonster仍旧属于根据类名来生成新对象。
  • 原型模式中的clone是根据现有对象来生成新对象。
  • 可以把原型模式看成是一种特殊的工厂方法模式。

2.5 原型模式优缺点

  • 如果创建的新对象内部数据比较复杂且多变,原型模式创建对象的效率可能会高很多。
  • 原型模式不存在额外的等级结构——原型模式不需要额外的工厂类。
  • clone接口的实现方法有多种。
  • 有些情况下,产品类中存在一个克隆方法也会给开发提供一些明显便利。