上篇文章中我们提到单例模式可以避免重复创建消耗资源的对象,但是却不得不共用对象。若是对象本身也不让随意访问修改时,怎么办?那么我们就可以采用原型模式来创建新的实例。
定义:原型模式用来创建重复对象,当直接创建对象的代价比较大的时候可以采用这种模式,这种模式实现了一个原型接口,这个接口用于创建当前对象的克隆,同时又能保证性能,属于一种创建型模式;
意图:用原型对象指定要创建对象的类型,并通过拷贝原型对象来创建新的对象;
主要解决:在直接创建对象代价比较大的时候采用这种模式;
关键代码:1、实现克隆Cloneable接口,重写Object中的clone方法,通过实现对象的浅拷贝或者通过序列化和反序列化来实现对象的深拷贝;2、原型模式同样用于隔离对象的使用者和具体类型之间的耦合关系,它同样要求这些“易变类”拥有稳定的接口;
应用实例:1、细胞分裂;
优点:1、性能提高;2、逃避构造函数的约束;
缺点:1、配备克隆方法需要对类的功能进行全面考虑,对于全新的类并不难,但是对于已有的类却不容易,例如引用中含有循环结构的时候;2、必须实现clone接口;
使用场景:1、资源优化场景,类的初始化需要消耗非常多的资源的时候,包括数据和硬件资源;2、性能和安全要求的场景;3、通过new产生一个对象需要非常频繁地数据准备或者权限访问的时候;4、一个对象提供给其他对象访问,而各个调用者都可能需要对其进行修改的时候;5、通常和工厂方法一起出现,通过原型模式创建对象,然后由工厂方法提供给调用者;
UML类图:
以下是Demo源码:
package cn.com.pep.model.prototype; /** * * @Title: Shape * @Description: * @author wwh * @date 2022-8-25 16:48:21 */ public abstract class Shape implements Cloneable { String id; String type; 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; } /** * @Title: draw * @Description: */ public abstract void draw(); @Override public Object clone() throws CloneNotSupportedException { try { return super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return null; } }
package cn.com.pep.model.prototype; /** * * @Title: Circle * @Description: * @author wwh * @date 2022-8-25 16:51:33 */ public class Circle extends Shape { public Circle() { type = "circle"; } @Override public void draw() { System.err.println("Draw a Circle!"); } }
package cn.com.pep.model.prototype; /** * * @Title: Rectangle * @Description: * @author wwh * @date 2022-8-25 16:55:02 */ public class Rectangle extends Shape { public Rectangle() { type = "rectangle"; } @Override public void draw() { System.err.println("Draw a rectangle!"); } }
package cn.com.pep.model.prototype; /** * * @Title: Square * @Description: * @author wwh * @date 2022-8-25 16:53:52 */ public class Square extends Shape { public Square() { type = "square"; } @Override public void draw() { System.err.println("Draw a Square!"); } }
package cn.com.pep.model.prototype; import java.util.Hashtable; /** * * @Title: ShapeFactory * @Description: * @author wwh * @date 2022-8-25 16:56:33 */ public class ShapeFactory { private static Hashtable<String, Shape> t = new Hashtable<>(); /** * 类加载的时候通过静态块完成原型对象的初始化 */ static { Circle circle = new Circle(); t.put("circle", circle); Square square = new Square(); t.put("square", square); Rectangle rectangle = new Rectangle(); t.put("rectangle", rectangle); } public static Shape getShape(String type) { Shape clone = null; try { Shape shape = t.get(type); clone = (Shape) shape.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return clone; } }
package cn.com.pep.model.prototype; /** * * @Title: PrototypePatternDemo * @Description: * @author wwh * @date 2022-8-25 17:02:02 */ public class PrototypePatternDemo { public static void main(String[] args) { Shape circle = ShapeFactory.getShape("circle"); System.err.println(circle); Shape square = ShapeFactory.getShape("square"); System.err.println(square); Shape rectangle = ShapeFactory.getShape("rectangle"); System.err.println(rectangle); } }
以上代码中采用了浅拷贝调用super.clone()方法来实现原型模式,被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用都仍然指向原来的对象,这就是浅拷贝,当然也可以采用序列化和反序列化来实现深拷贝通过原型对象生成目标对象,代码如下:
public Object clone() throws CloneNotSupportedException { // try { // return super.clone(); // } catch (CloneNotSupportedException e) { // e.printStackTrace(); // } // return null; ByteArrayOutputStream bos = null; ObjectOutputStream oos = null; ByteArrayInputStream bis = null; ObjectInputStream ois = null; Object obj = null; try { /* 序列化 */ bos = new ByteArrayOutputStream(); oos = new ObjectOutputStream(bos); oos.writeObject(this); /* 反序列化 */ bis = new ByteArrayInputStream(bos.toByteArray()); ois = new ObjectInputStream(bis); obj = ois.readObject(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { try { if (bos != null) { bos.close(); } if (oos != null) { oos.close(); } if (bis != null) { bis.close(); } if (ois != null) { ois.close(); } } catch (IOException e) { e.printStackTrace(); } } return obj; }
深拷贝是通过流来生成对象的,生成的对象就和原型对象完全是两个对象啦。