设计模式中有六大原则和二十三设计模式。
其中六大原则分别为:单一职责原则、开闭原则、里氏替换原则、依赖倒置原则、接口隔离原则、迪米特原则。
二十三设计模式:单例模式、Builder 模式、原型模式、工厂方法模式、抽象工厂模式、策略模式、状态模式、责任链模式、解释器模式、命令模式、观察者模式、备忘录模式、迭代器模式、模版方法模式、访问者模式、中介模式、代理模式、组合模式、适配器模式、装饰模式、享元模式、外观模式、桥接模式。
定义
- 用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。《Android 源码设计模式解析与实践》
- 原型模式是一种创建型设计模式,Prototype模式允许一个对象再创建另外一个可定制的对象,根本无需知道任何如何创建的细节,工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建。[1]
使用场景
- 类初始化需要消耗很多资源(数据、硬件资源等)。
- 通过 new 产生一个对象需要非常繁琐的数据准备或访问权限。
- 一个对象需提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象共调用者使用,即保护性拷贝。
说到拷贝,我们再说下深拷贝和浅拷贝。
深拷贝和浅拷贝
- 浅拷贝
创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。
常用的方法有:一一赋值、clone()等。
需要注意的是:对于我们常用【=】它不属于浅拷贝,属于赋值,是同一个对象。如果是基本类型我们拷贝的是它的值;如果是对象则是拷贝的是对象内存地址的引用,其实他们还是同一个对象。
- 深拷贝
创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。
常用的方法有:层层拷贝(每个元素都需要实现clone()
方法)、序列化反序列化、Gson 转换、构造函数(相当重新new一个对象,一一赋值)等
示例
- 对象类
/**
* 元素类
*/
public class Children implements Cloneable {
private String content;
@Override
protected Children clone() throws CloneNotSupportedException {
return (Children)super.clone();
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
/**
* 原型类
*/
public class Prototype implements Cloneable {
private String message;
private String title;
private Children children;
// ...
@Override
protected Prototype clone() throws CloneNotSupportedException {
// 浅拷贝
return (Prototype)super.clone();
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public Children getChildren() {
return children;
}
public void setChildren(Children children) {
this.children = children;
}
}
- 实现
public static void main(String[] args) throws CloneNotSupportedException {
Prototype prototype1 = new Prototype();
prototype1.setTitle("Title1");
prototype1.setMessage("Message1");
Children children = new Children();
children.setContent("Content1");
prototype1.setChildren(children);
System.out.println("prototype1------\n hashCode = "+prototype1.hashCode()+"\n title ="+prototype1.getTitle()+"\n message="+prototype1.getMessage());
System.out.println("Children1-------\n hashCode = "+prototype1.getChildren().hashCode()+"\n content="+prototype1.getChildren().getContent());
Prototype prototype2 = prototype1.clone();
System.out.println("prototype2------\n hashCode = "+prototype2.hashCode()+"\n title ="+prototype2.getTitle()+"\n message="+prototype2.getMessage());
System.out.println("Children2-------\n hashCode = "+prototype2.getChildren().hashCode()+"\n content="+prototype2.getChildren().getContent());
prototype2.getChildren().setContent("Content2");
System.out.println("prototype11------\n hashCode = "+prototype1.hashCode()+"\n title ="+prototype1.getTitle()+"\n message="+prototype1.getMessage());
System.out.println("Children11-------\n hashCode = "+prototype1.getChildren().hashCode()+"\n content="+prototype1.getChildren().getContent());
System.out.println("prototype22------\n hashCode = "+prototype2.hashCode()+"\n title ="+prototype2.getTitle()+"\n message="+prototype2.getMessage());
System.out.println("Children22-------\n hashCode = "+prototype2.getChildren().hashCode()+"\n content="+prototype2.getChildren().getContent());
}
- 结果
prototype1------
hashCode = 531885035
title =Title1
message=Message1
Children1-------
hashCode = 1418481495
content=Content1
prototype2------
hashCode = 303563356
title =Title1
message=Message1
Children2-------
hashCode = 1418481495
content=Content1
prototype11------
hashCode = 531885035
title =Title1
message=Message1
Children11-------
hashCode = 1418481495
content=Content2
prototype22------
hashCode = 303563356
title =Title1
message=Message1
Children22-------
hashCode = 1418481495
content=Content2
从上边的例子我们可以看出 Prototype 的 clone() 方法只是创建它自己本身新对象,而它的元素 Children 还是指向了原有的地址,这就是浅拷贝。要是想实现深拷贝,只需要重写 Prototype 的 clone() 方法。
@Override
protected Prototype2 clone() throws CloneNotSupportedException {
// 浅拷贝
Prototype2 prototype2 = (Prototype2)super.clone();
prototype2.children = this.children.clone();
return prototype2;
}
prototype1------
hashCode = 531885035
title =Title1
message=Message1
Children1-------
hashCode = 1418481495
content=Content1
prototype2------
hashCode = 303563356
title =Title1
message=Message1
Children2-------
hashCode = 135721597
content=Content1
prototype11------
hashCode = 531885035
title =Title1
message=Message1
Children11-------
hashCode = 1418481495
content=Content1
prototype22------
hashCode = 303563356
title =Title1
message=Message1
Children22-------
hashCode = 135721597
content=Content2
以上对原型模式(深拷贝、浅拷贝)进行了举例。接下来我们再说下它的优缺点。
优缺点
优点
- 性能高:原型模式是在内存中二进制流的拷贝,要比直接new 一个对象性能好,特别是在循环体中产生大量对象时,
- 简化流程:可以利用深拷贝,实现状态记录、回退等功能。
缺点
- 构造函数不会执行:原型模式是在内存中进行拷贝的,会跳过构造函数。
- 实现繁琐:必须实现clone()方法
- 拷贝风险:浅拷贝和深拷贝,一旦使用不正确,就会造成大灾难。
总结
浅拷贝和深拷贝有多种实现方法,这里只用一种方法举例,其他的方法可以参照深拷贝和浅拷贝。
原型模式的宗旨就是对象的拷贝,节省创建多次创建新对象带来的消耗,提高性能。但是也需要注意深浅拷贝带来的问题。
参考
- 原型模式
- 《Android 源码设计模式解析与实践》