写在前面
但我们看某个电影,或者是某个电视剧的时候,总会提到某某人是某某角色的原型,这里某某角色就好像是某某人的复制品
一样,这里的原型设计模式也是如此,不过,这里的原型
是一个对象,而原型设计模式就是指复制这个原型对象
,生成一个新的对象。本文就一起来看下吧!
1:介绍
1.1:什么时候使用原型设计模式
当对象的创建成本比较大,比如一个对象的创建需要依赖于其它服务的调用,需要依赖于数据库的查询,则此时,就可以考虑使用原型设计模式,通过拷贝(克隆)的方式来生成新的对象。实际上,在java中,原型设计模式的实现是非常简单的,因为java天生支持克隆,只需要实现java.lang.Clonable
接口标记该对象可以被克隆,则就可以调用clone方法(来自Object的native方法)
来克隆对象了。
1.2:UML类图
原型设计模式,包含如下元素:
1:抽象原型类
提供一个克隆方法的接口,在java中就是java.lang.Clonable
2:具体原型类
实现抽象原型类,并实现其克隆方法,完成克隆
3:客户端类
使用具体原型类的克隆方法完成对象的克隆,获取新对象
UML图如下:
2:实例
源码 。
2.1:场景
假定我们现在有了一个经过非常复杂的逻辑,经过数据库查询才创建出来的一个对象,并且该对象的创建每次都是如此,这里我们就是用原型设计模式来创建新的对象,避免性能损耗,以及提高对象创建的速度。
2.2:程序
- 抽象原型类
即java.lang.Clonable,不需要我们额外编写了,如下:
public interface Cloneable {
}
- 具体原型类
public class Thing2 implements Cloneable {
private ArrayList<String> list = new ArrayList<String>();
@Override
public Thing2 clone() {
Thing2 thing = null;
try {
thing = (Thing2) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
System.out.println("克隆失败");
}
return thing;
}
public void setValue(String value) {
this.list.add(value);
}
public ArrayList getValue() {
return this.list;
}
}
- 客户端类
public class SimpleClone {
public static void main(String[] args) {
Thing2 thing = new Thing2();
thing.setValue("张三");
Thing2 cloneThing = thing.clone();
cloneThing.setValue("李四");
System.out.println("原始对象:" + thing.getValue());
System.out.println("拷贝对象:" +cloneThing.getValue());
}
}
运行:
原始对象:[张三, 李四]
拷贝对象:[张三, 李四]
注意到,拷贝出来的对象,添加了李四
,也影响了原始的对象,出现这个问题的原因是java的克隆是浅拷贝
,即对象类型和数组类型是不拷贝的,而这里的List就是对象类型,这里其实是个坑,很容易掉进去,因此我们要用深拷贝
,做法也比较简单,直接对对象类型再拷贝重新赋值到克隆对象即可,修改clone方法如下:
@Override
public Thing1 clone() {
Thing1 thing = null;
try {
thing = (Thing1) super.clone();
thing.list = (ArrayList) this.list.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
System.out.println("克隆失败");
}
return thing;
}
thing.list = (ArrayList) this.list.clone();
这行代码就完成了深拷贝,此时再运行,结果如下:
原始对象:[张三]
拷贝对象:[张三, 李四]
3:原型设计模式变种
当具体原型对象,不是一个,而是多个时,为了方便获取克隆的原型对象,增加了原型管理器的角色,客户端类不需要自己调用克隆方法生成对象,而是通过原型管理器来获取克隆的对象,如下图:
先来定义两个具体产品类(有接口):
public interface Shape extends Cloneable {
public Object clone(); //拷贝
public void countArea(); //计算面积
}
public class Circle implements Shape {
public Object clone() {
Circle w = null;
try {
w = (Circle) super.clone();
} catch (CloneNotSupportedException e) {
System.out.println("拷贝圆失败!");
}
return w;
}
public void countArea() {
int r = 0;
System.out.print("这是一个圆,请输入圆的半径:");
Scanner input = new Scanner(System.in);
r = input.nextInt();
System.out.println("该圆的面积=" + 3.1415 * r * r + "\n");
}
}
public class Square implements Shape {
public Object clone() {
Square b = null;
try {
b = (Square) super.clone();
} catch (CloneNotSupportedException e) {
System.out.println("拷贝正方形失败!");
}
return b;
}
public void countArea() {
int a = 0;
System.out.print("这是一个正方形,请输入它的边长:");
Scanner input = new Scanner(System.in);
a = input.nextInt();
System.out.println("该正方形的面积=" + a * a + "\n");
}
}
定义原型管理器类(有点工厂的意思了)
:
public class ProtoTypeManager {
private HashMap<String, Shape> ht = new HashMap<String, Shape>();
public ProtoTypeManager() {
ht.put("Circle", new Circle());
ht.put("Square", new Square());
}
public void addshape(String key, Shape obj) {
ht.put(key, obj);
}
public Shape getShape(String key) {
Shape temp = ht.get(key);
// 每次克隆新的,但是注意,这里是浅拷贝,实际中还是深拷贝,避免出现多个对象的引用类型属性指向同一个对象的情况
return (Shape) temp.clone();
}
}
测试:
public class ProtoTypeShape {
public static void main(String[] args) {
ProtoTypeManager pm = new ProtoTypeManager();
Shape obj1 = (Circle) pm.getShape("Circle");
obj1.countArea();
Shape obj2 = (Shape) pm.getShape("Square");
obj2.countArea();
}
}
输出:
这是一个圆,请输入圆的半径:该圆的面积=28.2735
这是一个正方形,请输入它的边长:该正方形的面积=81