写在前面
但我们看某个电影,或者是某个电视剧的时候,总会提到某某人是某某角色的原型,这里某某角色就好像是某某人的复制品
一样,这里的原型设计模式也是如此,不过,这里的原型
是一个对象,而原型设计模式就是指复制这个原型对象
,生成一个新的对象。本文就一起来看下吧!
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>();@Overridepublic 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
写在后面
参考文章列表
设计模式之原型模式(Prototype)详解及代码示例 。