1、模式简介
原型模式的定义:
通过复制一个现有的对象(原型)来得到一个相似的对象。
原型模式的UML图如下图所示:
从上图中可以看到,所有的对象实体类都是继承自一个Prototype的父类,而prototype类有一个可以复制对象的方法clone(),可以复制其任意子类。这样,所有Prototype类的子类在整个程序中只需要new一次,其他情况下只需要从这几个类中clone即可。
原型模式的适用场景:
- 当系统中需要重复的使用某一些类的对象时;
- 当这些对象的差别不是很大或完全相同时;
- 当一个类的对象必须在某几种状态之中时;
- 当系统需要对对象进行缓存时。
原型模式的优点:
- 克隆一般是在内存中以流的形式进行的,比创建一个对象速度快,因此原型模式可以提高系统的性能;
- 克隆对象的时候可以完全忽略类中的构造方法的访问权限,因此逃避构造函数的约束。
原型模式的缺点:
- 克隆并不是对所有的数据类型都适用,有些内部类(如ArrayList)是不支持克隆的;
- 克隆会忽略构造函数的约束,因此对于一些单例的类也会生成克隆对象,容易导致系统错误。
2、案例代码
需求:
有三种形状圆形(Circle)、矩形(Rectangle)和三角形(Triangle)。多次调用这些形状排成一个队列集合。
分析:
我们可以把这个题目中的几个逻辑想象成如下图所示的关系:
我们让圆形(Circle)、矩形(Rectangle)和三角形(Triangle)三种图形继承Shape父类,然后在缓存ShapeCache类中的HashMap中分别缓存三种形状,当主函数中需要某种形状的时候,我们就从缓存中克隆一个使用。
说明:
在JAVA中有一个Cloneable接口,是专门用来实现原型模式的。只需要让一个类实现这个接口,重写Object类的clone()方法,就可以方便的实现原型模式了。
代码:
1) 原型父类Shape:
public class Shape implements Cloneable {
protected String type; @Override
protected Object clone() {
Shape shape = null;
try {
shape = (Shape) super.clone();
}catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return shape;
} public void introduce() {
System.out.println("This is a " + type);
}
}
2) Shape的子类(以Circle类为例):
public class Circle extends Shape { public Circle() {
super.type = "Circle";
}
}
3) 缓存类ShapeCache:
public class ShapeCache {
private Map<String, Shape> shapes; public ShapeCache() {
loadCache();
} private void loadCache() {
shapes = new HashMap<>();
shapes.put("circle", new Circle());
shapes.put("rectangle", new Rectangle());
shapes.put("triangle", new Triangle());
} public Shape getShape(String id) {
Shape shape = shapes.get(id);
return (Shape) shape.clone();
}
}
4) 测试类Test:
public class Test {
public static void main(String[] args) {
List<Shape> shapeList = new ArrayList<>(); ShapeCachecache = new ShapeCache();
shapeList.add(cache.getShape("triangle"));
shapeList.add(cache.getShape("circle"));
shapeList.add(cache.getShape("rectangle"));
shapeList.add(cache.getShape("circle"));
shapeList.add(cache.getShape("rectangle"));
shapeList.add(cache.getShape("triangle"));
shapeList.add(cache.getShape("rectangle"));
shapeList.add(cache.getShape("circle")); for (Shape shape : shapeList) {
shape.introduce();
}
}
}
5) 运行结果如下图所示:
3、扩展
上面说过,JAVA中为我们提供了一个Cloneable接口,只有实现了这个接口之后,重写Object类中的clone()方法才有效,否则会报CloneNotSupportedException异常。
使用原型模式复制对象时不会调用类的构造方法,因为复制出来的对象是原型对象复制粘贴出来的,是直接在内存中复制数据,因此不会调用到类的构造方法,甚至连访问权限都对原型模式无效,这一点在编程时需要特别注意。
Object类的clone()方法只能拷贝对象中的8种基本数据类型,对于数组、容器、引用对象等都不会拷贝,这种现象被成为浅拷贝。与浅拷贝对应的是深拷贝,即在对对象进行浅拷贝之后,将对象中的数组、容器、引用对象单独进行拷贝。
最后贴出原型模式的GitHub地址:【GitHub - Prototype】。