• 原型模式 创建型模式之二


    1.定义

            DP书上的定义为:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。其中有一个词很重要,那就是拷贝(既clone)。可以说,拷贝(clone)是原型模式的精髓所在。其中拷贝分为深拷贝和浅拷贝。

    浅复制:被复制的对象的所有变量都含有与原来的对象相同的值,而所有对其他对象的引用都仍然指向原来的对象。

    深复制:把引用对象的变量指向复制过的新对象,而不是原有的被引用的对象。Clone的时候使用深复制。

    2.应用场景

            创建型模式一般是用来创建一个新的对象,然后我们使用这个对象完成一些对象的操作,我们通过原型模式可以快速的创建一个对象而不需要提供专门的new()操作就可以快速完成对象的创建,而且也不需要知道任何创建的细节。这无疑是一种非常有效的方式,快速的创建一个新的对象。一般在初始化的信息不发生变化的情况下,克隆是最好的办法。这既隐藏了对象创建的细节,有对性能是大大的提高。因为如果不用Clone,每次new,都需要执行一次构造函数,如果构造函数的执行时间很长,那么多次的执行这个初始化操作就实在是太低效了。新的对象(其实它是一个指针),因为直接声明新的对象必然会启用构造函数。

            当通过初始化对象时需要消耗非常多的资源的时候,包括软硬件资源当通过new 一个对象需要准备繁琐的数据或复杂的权限时可以使用原型模式;当一个对象需要提供给其他对象访问的时候,而且各个调用者可能会改变其属性的时候可以考虑使用原型模式构造多个副本对象供其他对象调用,又称为保护性拷贝。使用原型模式时,引用的成员变量必须满足两个条件才会被拷贝:一是类的成员变量,而不是方法内变量;二是必须是一个可变的引用对象,而不是一个原始类型或不可变对象。即final修饰的不会被拷贝。

    3.优缺点

    优点:
            性能优良,原型模式是在内存二进制流的拷贝,当new的对象比较复杂的时候,要比直接new一个对象性能好很多,特别是在一个循环体内产生大量的对象时,原型模式可以更好的体现其优点。原型模式允许在运行时动态改变具体的实现类型。原型模式可以在运行期间,由客户来注册符合原型接口的实现类型,也可以动态地改变具体的实现类型,看起来接口没有任何变化,但其实运行的已经是另外一个类实例了。因为克隆一个原型就类似于实例化一个类。
    对客户隐藏制造新实例的复杂性
    缺点:
            由于是直接在内存中拷贝,构造函数不会执行。原型模式最主要的缺点是每一个类都必须配备一个克隆方法。配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类来说不是很难,而对于已经有的类不一定很容易,特别是当一个类引用不支持序列化的间接对象,或者引用含有循环结构的时候,耦合度较高。

    4.示例代码

    1. #include
    2. #include
    3. #include
    4. #include
    5. class Resume
    6. {
    7. protected:
    8. std::string name;
    9. std::string skill;
    10. //年龄,工作年限,项目等省略...
    11. public:
    12. Resume() = default;
    13. Resume(const std::string& name, const std::string& skill)
    14. : name{ name }
    15. , skill{ skill }
    16. {
    17. }
    18. virtual ~Resume() = default;
    19. virtual void setName(const std::string&) = 0;
    20. virtual void setSkill(const std::string&) = 0;
    21. virtual void show() = 0;
    22. virtual std::unique_ptr clone() = 0;
    23. };
    24. class ResumeA : public Resume
    25. {
    26. public:
    27. ResumeA(const std::string& name, const std::string& skill) :Resume{ name, skill }
    28. {
    29. //有的构造函数需要很长的初始化时间,这时候采用原型模式很合适
    30. for (int i = 0; i < 10000; i++)
    31. {
    32. }
    33. printf("init ...\n");
    34. this->name = name;
    35. this->skill = skill;
    36. }
    37. ResumeA(const ResumeA& rhs)
    38. {
    39. this->name = rhs.name;
    40. this->skill = rhs.skill;
    41. }
    42. ~ResumeA() = default;
    43. void setName(const std::string& name)
    44. {
    45. this->name = name;
    46. }
    47. void setSkill(const std::string& skill)
    48. {
    49. this->skill = skill;
    50. }
    51. void show()
    52. {
    53. std::cout << "ResumeA name : " << name << ", skill: " << skill << std::endl;
    54. }
    55. std::unique_ptr clone() //克隆,关键所在
    56. {
    57. return std::make_unique(*this);
    58. }
    59. };
    60. int main()
    61. {
    62. auto r1 = new ResumeA("PersionA", "精通C,熟悉C++...");
    63. auto r3 = r1->clone();
    64. r1->show();
    65. //想去面试C++要求高的公司
    66. r3->setSkill("精通C++ STL, design pattern...");
    67. r3->show();
    68. return 0;
    69. }

    5.引用

    https://blog.csdn.net/janeqi1987/article/details/103933624

     

  • 相关阅读:
    Java--Mybatis中 #{} 和 ${} 的区别
    正则表达式re总结
    电脑技巧:Win10自带的6个实用功能,你都会用吗
    【附源码】Python计算机毕业设计社区团购配送与管理系统
    【服务端 | Redis】如何使用redis 有序集合实现股票交易的订单表(价格优先、时间优先)
    前端安全:CSRF、XSS该怎么防御?
    C语言错题笔记
    机器学习入门介绍
    【管理运筹学】第 9 章 | 网络计划(2,时间参数的计算 —— 工作时间的确定与事项的时间参数)
    docker-compose快速部署nginx
  • 原文地址:https://blog.csdn.net/Physics_ITBoy/article/details/133278470