概述:
原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式之一。
这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。
特点:
原型模式是用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。主要解决在运行期建立和删除原型。
优点:
- 性能提高。
- 逃避构造函数的约束。
缺点:
- 配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候。
- 必须实现 Cloneable 接口。
使用场景:
- 资源优化场景。
- 类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。
- 性能和安全要求的场景。
- 通过 new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。
- 一个对象多个修改者的场景。
- 一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。
- 在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过 clone 的方法创建一个对象,然后由工厂方法提供给调用者。
实现方法:
原型模式的实现方式十分简单
原型接口必须有一个可以克隆自己的方法,反回类型为Prototype
public interface Prototype {
public Object clone();
}
然后是需要一个原型类供其他人克隆引用,所以要实现 Prototype
接口,具备克隆的能力
/**
* @Author mzh
* @Date 2023/3/25
*/
public class Plane implements Prototype{
/**
* 飞机名字
*/
private String name;
/**
* 飞机重量
*/
private String height;
public Plane(){
this.name = "Name:" + Math.random();
this.height = "height:" + Math.random();
}
public Plane(Plane plane){
this.name = plane.getName();
this.height = plane.getHeight();
}
public String getName() {
return name;
}
public String getHeight() {
return height;
}
@Override
public Object clone() {
return new Plane(this);
}
}
然后就是客户端的引用
/**
* @Author mzh
* @Date 2023/3/25
*/
public class PrototypeDemoTest {
public static void main(String[] args) {
Plane plane = new Plane();
System.out.println(plane.getName());
System.out.println(plane.getHeight());
Plane clone = (Plane) plane.clone();
System.out.println(clone.getName());
System.out.println(clone.getHeight());
}
}
运行客户端的代码,可以得到运行结果
Name:0.9873778434480015
height:0.8991139462401434
Name:0.9873778434480015
height:0.8991139462401434
由运行结果可以发现,克隆类实际和被克隆的对象内容是相同的。
深拷贝与浅拷贝
与通过对一个类进行实例化来构造新对象不同的是,原型模式是通过拷贝一个现有对象生成新对象的。浅拷贝实现 Cloneable,重写,深拷贝是通过实现 Serializable 读取二进制流。
- 浅拷贝:当拷贝对象只包含简单的数据类型比如int、float 或者不可变的对象(字符串)时,就直接将这些字段复制到新的对象中。而引用的对象并没有复制而是将引用对象的地址复制一份给克隆对象
- 深拷贝:不管拷贝对象里面简单数据类型还是引用对象类型都是会完全的复制一份到新的对象中