在日常软件系统开发中,某些对象的创建new即为复杂,而且我们需要使用多个相同的实例。这样如果我们依旧使用new去进行创建。就会增加系统的复杂度和增加代码之间的耦合度。但是我们使用前面介绍的工厂模式的话,随着产品类型的增多,子类也会随之增多。维护代码的难度也就会越来越大,所以这里暂时不适合使用工厂模式。由于实例都是一样的,类型相同,但只是实例的参数和状态可能不同,这时若已经有一个对象,我们便可以结合下面讲述的原型模式,即通过一个原型对象,克隆拷贝出许多其他相同的对象来。
原型模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。
例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。
一、来由
在面对软件系统设计的过程中,会面临着”复杂对象”的创建,然而对象可能会由于需求的变动而进行变动。那么我们又需要保持接口的稳定性。这时如何保证应对这种变化呢?
创建型模式一般是用来创建一个新的对象,然后我们使用这个对象完成一些对象的操作,我们通过原型模式可以快速的创建一个对象而不需要提供专门的new()操作就可以快速完成对象的创建,这无疑是一种非常有效的方式,快速的创建一个新的对象。
二、定义
原型模式是创建型模式中的一种较为特殊的模式,用于 克隆现有的对象 。
Specify the kind of objects to create using a prototypical instance, and create new objects by copying this prototype. (使用原型实例指定将要创建的对象类型,通过复制这个实例创建新的对象。)
在使用原型模式克隆对象时,根据其成员对象是否也克隆,原型模式可以分为两种形式:深克隆 和 浅克隆 。
浅拷贝:只复制指针值而不是实际资源。即只拷贝对象中的基本数据类型(8种),对于数组、容器、引用对象等都不会拷贝。
深拷贝:不仅复制指针值,还复制指针所指向的资源。即不仅能拷贝基本数据类型,还能拷贝那些数组、容器、引用对象等。
三、角色分析(可认为是“三个角色”,也可认为是“两个角色”,抽象原型类 和 具体原型类 可合并成一个原型类):
1 、Prototype(抽象原型类)
Prototype 角色负责定义用于复制现有实例来生成新实例的方法,使其拥有被克隆复制的功能。同时,此角色定义了具体原型类所需的实现的方法。
2 、Concrete Prototype(具体原型类)
Concrete Prototype 角色负责实现复制现有实例并生成新实例的方法。实现抽象原型角色的克隆接口,或者继承抽象原型类。
3 、Client(客户类/使用者)
Client 角色负责使用复制实例的方法生成新的实例,即用户使用复制粘贴的功能。
四、类图
五、优点:
1、性能提高。当创建新的对象实例较为复杂时,使用原型模式可以简化对象的创建过程,通过一个已有实例可以提高新实例的创建效率,也可以动态增加或减少产品类,也可以使用深克隆的方式保存对象的状态。
2、逃避构造函数的约束。
六、缺点:
1、必须实现 Cloneable 接口,即需要为每一个类配备一个克隆方法,在实现深克隆时需要编写较为复杂的代码。
2、配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对已有的类进行改造时,不一定是件容易的事,必须修改其源代码,违背了“开闭原则”。
七、实际应用案例
1、原型模式应用于很多软件中,如果每次创建一个对象要花大量时间,原型模式是最好的解决方案。很多软件提供的复制(Ctrl + C)和粘贴(Ctrl + V)操作就是原型模式的应用,复制得到的对象与原型对象是两个类型相同但内存地址不同的对象,通过原型模式可以大大提高对象的创建效率。
2、在Struts2中为了保证线程的安全性,Action对象的创建使用了原型模式,访问一个已经存在的`Action对象时将通过克隆的方式创建出一个新的对象,从而保证其中定义的变量无须进行加锁实现同步,每一个Action中都有自己的成员变量,避免Struts1因使用单例模式而导致的并发和同步问题。
3、在Spring中,用户也可以采用原型模式来创建新的bean实例,从而实现每次获取的是通过克隆生成的新实例,对其进行修改时对原有实例对象不造成任何影响。
参考:
共同学习,共同进步,若有补充,欢迎指出,谢谢!