文章目录
设计模式是面向问题、场景而总结产生的设计思路。是解决问题的套路。23 种经典的设计模式。它们又可以分为三大类:创建型、结构型、行为型。
创建型 包含了 单例模式、工厂模式、建造者模式、原型模式。
原型模式
1. 为什么使用原型模式
原型模式也称为克隆模式。
当我们把一个对象赋值给一个新的变量时,赋的其实是该对象的在栈中的地址,而不是堆中的数据。由于简单赋值对象,其实只是复制了对象的引用,还是指向同一个对象,当需要对对象进行操作时会互相影响。当需要在创建大量相同的对象时,如果用传统的构造函数来创建对象,会比较复杂且耗时耗资源。使用原型模式创建对象性能上要好的多,因为Object类的clone方法是一个本地方法,它直接操作内存中的二进制流,特别是复制大对象时,性能的差别非常明显。值得注意的是,使用clone方法创建的新对象的构造函数是不会被执行的,也就是说会绕过任何构造函数(有参和无参),因为clone方法的原理是从堆内存中以二进制流的方式进行拷贝,直接分配一块新内存。
2. 原型模式适用的场景
- 需要复制的对象状态相同或相似。系统中大量使用该类对象,且各个调用者都需要定义对象状态。
- 创建对象成本很耗资源,例如初始化时间长,占用CPU太多,或者占用网络资源太多等,需要优化资源。
- 创建对象成本很繁琐,创建一个对象需要繁琐的数据准备或访问权限等。
3. Clone 方法 和 Cloneable 接口
实现原型模式需要使用clone方法,并实现Cloneable 接口。
3.1 重写 clone 方法
Java中,所有类的父类都是Object类,Object类中有一个clone方法,作用是返回对象的一个拷贝,但是其作用域protected类型的,一般的类无法调用,因此,子类在继承重写时需要将clone方法的作用域修改为public类型,以便于外部调用。虽然子类对父类方法的权限做了提升,违法了lsp原则,但是相应的也提高了系统的灵活性。
protected native Object clone() throws CloneNotSupportedException;
3.2 实现 Cloneable 接口
在java语言有一个Cloneable接口。在java虚拟机中,只有实现了这个接口的类才可以被拷贝,否则在运行时会抛出CloneNotSupportedException异常。通过实现该接口,在运行时安全地在调用clone方法。
public interface Cloneable {
}
4 浅拷贝和深拷贝
4.1 浅拷贝
浅拷贝:基本属性被克隆,但对于引用属性,仍指向原有属性所指向的对象的内存地址。
当数组arr的克隆被去掉后,arr会被共享使用。
public ComplexObject clone() throws CloneNotSupportedException {
ComplexObject prototype = (ComplexObject)super.clone();
//prototype.arr = this.arr.clone();
return prototype;
}
通过客户端调用,修改基本属性和引用对象。发现基本属性是被克隆,但是引用属性仍然是共享的。
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
ComplexObject o = new ComplexObject(2,"Newton",new int[]{1,2,3,4});
ComplexObject o1 = o.clone();
o1.getArr()[2]=5;
o1.setName("牛顿");
System.out.println(o);
System.out.println(o1);
}
}
运行显示。一旦修改arr,两个对象的o都会被修改。
ComplexObject{id=2, name='Newton', arr=[1, 2, 5, 4]}
ComplexObject{id=2, name='牛顿', arr=[1, 2, 5, 4]}
4.2 深拷贝
深拷贝:属性中引用类型的属性也会被克隆。
public ComplexObject clone() throws CloneNotSupportedException {
ComplexObject prototype = (ComplexObject)super.clone();
prototype.arr = this.arr.clone();
return prototype;
}
通过深克隆后,arr属性没有共享,独立拥有arr数组对象。
运行结果:
ComplexObject{id=2, name='Newton', arr=[1, 2, 3, 4]}
ComplexObject{id=2, name='牛顿', arr=[1, 2, 5, 4]}
总结
原型模式非常适合于你想要向客户隐藏实例创建的创建过程、或者需要快速复制对象的场景。
设计模式系列在github上有一个开源项目,主要是本系列博客的demo代码。https://github.com/forestnlp/designpattern
如果您对软件开发、机器学习、深度学习有兴趣请关注本博客,将持续推出Java、软件架构、深度学习相关专栏。
您的支持是对我最大的鼓励。