简介
原型模式(Prototype Pattern):使用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。原型模式是一种对象创建型模式。
原理: 将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝自己来实现创建过程。
由于在软件系统中我们经常会遇到需要创建多个相同或者相似对象的情况,因此原型模式在真实开发中的使用频率还是非常高的。
原型模式是一种“另类”的创建型模式,创建克隆对象的工厂就是原型类自身,工厂方法由克隆方法来实现。
需要注意的是通过克隆方法所创建的对象是全新的对象,它们在内存中拥有新的地址,通常对克隆所产生的对象进行修改对原型对象不会造成任何影响,每一个克隆对象都是相互独立的。通过不同的方式修改可以得到一系列相似但不完全相同的对象。
原型模式中的几个角色:
●Prototype(抽象原型类):它是声明克隆方法的接口,是所有具体原型类的公共父类,可以是抽象类也可以是接口,甚至还可以是具体实现类。
● ConcretePrototype(具体原型类):它实现在抽象原型类中声明的克隆方法,在克隆方法中返回自己的一个克隆对象。
● Client(客户类):让一个原型对象克隆自身从而创建一个新的对象,在客户类中只需要直接实例化或通过工厂方法等方式创建一个原型对象,再通过调用该对象的克隆方法即可得到多个相同的对象。由于客户类针对抽象原型类Prototype编程,因此用户可以根据需要选择具体原型类,系统具有较好的可扩展性,增加或更换具体原型类都很方便。
简单示例
1.定义抽象原型类,声明克隆方法。
public abstract class Shape implements Cloneable {
private String id;
protected String type;
abstract void draw();
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Object clone = null;
clone = super.clone();
return clone;
}
}
2.定义具体原型类,需要继承抽象原型类。
public class Circle extends Shape {
@Override
void draw() {
System.out.println("circle draw method");
}
public Circle() {
type = "Circle";
}
}
public class Square extends Shape {
@Override
void draw() {
System.out.println("Square draw method");
}
public Square() {
type = "Square";
}
}
3.创建一个类,从数据库获取实体类,并把它们存储在一个Hashtable中。
public class ShapeProtoType {
//模拟数据库查询获取
private static Hashtable<String, Shape> shapeMap = new Hashtable<String, Shape>();
public static Shape getShape(String shapeId) throws CloneNotSupportedException {
Circle circle = new Circle();
circle.setId("1");
Square square = new Square();
square.setId("2");
shapeMap.put(circle.getId(),circle);
shapeMap.put(square.getId(),square);
Shape cachedShape = shapeMap.get(shapeId);
return (Shape) cachedShape.clone();
}
4.测试验证
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
Shape clonedShape = (Shape) ShapeProtoType.getShape("1");
System.out.println("Shape : " + clonedShape.getType());
Shape clonedShape2 = (Shape) ShapeProtoType.getShape("2");
System.out.println("Shape : " + clonedShape2.getType());
}
}
分析
原型模式的实现过程中,要知道两个概念:
浅拷贝:被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用都仍然指向原来的对象。
ArrayList<String> a = new ArrayList();
ArrayList<String> b = a;
//当修改a时,b的值同样会被修改
深拷贝:把引用对象的变量指向复制过来的新对象,而不是原有的被引用的对象。
ArrayList<String> a = new ArrayList();
//使用Object类的super.clone()方法来使实现拷贝的过程
ArrayList<String> b = a.clone();
//使用a再创建了一个对象并赋值给c,这样a和c只是两个值相同的两个对象。
ArrayList<String> c = new ArrayList(a);
原型模式适用场景与优缺点
使用场景:
1。创建对象成本比较大,比如初始化要很长时间的,占用太多CPU的,新对象可以通过复制已有的对象获得的,如果是相似的对象,则可以对其成员变量稍作修改。
2。系统要保存对象状态的,而对象的状态改变很小。
3。需要避免使用分层次的工厂类来创建分层次的对象,并且类的对象就只用一个或很少的组合状态。
原型模式的优点
原型模式允许在运行时动态改变具体的实现类型。原型模式可以在运行期间,由客户来注册符合原型接口的实现类型,也可以动态地改变具体的实现类型,看起来接口没有任何变化,但其实运行的已经是另外一个类实例了。因为克隆一个原型就类似于实例化一个类。
原型模式的缺点
原型模式最主要的缺点是每一个类都必须配备一个克隆方法。配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类来说不是很难,而对于已经有的类不一定很容易,特别是当一个类引用不支持序列化的间接对象,或者引用含有循环结构的时候。