
目录什么是原型模式原型模式的实现原型模式分类浅克隆和深克隆的区别原型模式浅克隆原型模式深克隆原型模式简单实现方式原型模式登记实现方式原型模式的特点特点优点缺点应用场景什么是原型模式原型模式Prototype Pattern是用于创建重复的对象同时又能保证性能。这种类型的设计模式属于创建型模式它提供了一种创建对象的最佳方式之一。用原型实例指定创建对象的种类并且通过拷贝这些原型创建新的对象允许一个对象再创建另外一个可定制的对象无需知道如何创建的细节。原型模式简化了创建对象的过程通过一个已有的实例进行复制提高了创建实例的效率具有较好的可扩展性。通过将一个原型对象传给那个要发动创建的对象这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建即 对象.clone()用于创建当前对象的克隆。当直接创建对象的代价比较大时则采用这种模式。例如一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象在下一个请求时返回它的克隆在需要的时候更新数据库以此来减少数据库调用。原型模式的实现原型模式分类浅克隆创建一个新对象新对象的属性和原来对象完全相同对于非基本类型属性仍指向原有属性所指向的对象的内存地址。深克隆创建一个新对象属性中引用的其他对象也会被克隆不再指向原有对象地址。浅克隆和深克隆的区别使用浅克隆时当原型对象被复制时只复制它本身和其中包含的值类型的成员变量而引用类型的成员变量并没有复制而使用深克隆时除了对象本身被复制外对象所包含的所有成员变量也将被复制。在java 中浅克隆是继承Cloneable接口后重写clone方法完成的克隆而深克隆需要继承序列化接口Serializable使类可以序列化然后将类写入流中再从流中取出完成克隆。下面我们用代码展示浅克隆和深克隆。原型模式浅克隆/** * author Evan Walker * version 1.0 * desc 昂焱数据: https://www.ayshuju.com * date 2023/04/10 14:17:40 */ public class Room implements Cloneable { public Room room; private String name; private int size; private String color; private String address 上海市奉贤区奉城镇南奉公路686号4幢; public Room() { } public Room(String name, int size, String color) { super(); this.name name; this.size size; this.color color; } public String getName() { return name; } public void setName(String name) { this.name name; } public int getSize() { return size; } public void setSize(int size) { this.size size; } public String getColor() { return color; } public void setColor(String color) { this.color color; } Override public String toString() { return Sheep [name name , size size , color color , address address ]; } /** * 重写clone方法或者用默认的clone方法也行只不过调用时候需要抛异常 */ Override protected Object clone() { Room room null; try { room (Room) super.clone(); } catch (Exception e) { System.out.println(e.getMessage()); } return room; } public static void main(String[] args) { System.out.println(原型模式完成对象的创建); Room room1 new Room(昂焱数据, 1, 白色); room1.room new Room(昂焱数据, 2, 黑色); Room room2 (Room) room1.clone(); Room room3 (Room) room1.clone(); System.out.println(room1.getColor().hashCode() room2.getColor().hashCode()); System.out.println(room1.getColor() room2.getColor()); System.out.println(room1.getName().hashCode() room2.getName().hashCode()); room1.room.setName(昂焱科技); System.out.println(room1.room.getName()); System.out.println(room2.room.getName()); System.out.println(room1.room.getName().hashCode() room2.room.getName().hashCode()); System.out.println(room1.room.getName() room2.room.getName()); System.out.println(room1.room room1.room room1.room room1.room.hashCode()); System.out.println(room2.room room2.room room2.room room2.room.hashCode()); System.out.println(room3.room room3.room room3.room room3.room.hashCode()); System.out.println(room1 room1 room room1.hashCode()); System.out.println(room2 room2 room2 room2.hashCode()); System.out.println(room3 room3 room3 room3.hashCode()); } }从截图可以得知原型类与克隆类两者对应的成员变量的内存地址是一样的也就是说浅克隆时并未对原型类中的成员变量进行克隆具体原因是因为原型模式使用浅克隆时只复制它本身和其中包含的值类型的成员变量而引用类型的成员变量并不会去复制。原型模式深克隆import java.io.*; /** * author Evan Walker * version 1.0 * desc 深克隆工具类 昂焱数据: https://www.ayshuju.com * date 2023/04/10 20:38:47 */ public class CloneUtils { SuppressWarnings(unchecked) public static T extends Serializable T deepClone(T obj) { T cloneObj null; ObjectOutputStream obs null; ObjectInputStream ois null; try { //写入字节流 ByteArrayOutputStream out new ByteArrayOutputStream(); obs new ObjectOutputStream(out); obs.writeObject(obj); //分配内存写入原始对象生成新对象 ByteArrayInputStream ios new ByteArrayInputStream(out.toByteArray()); ois new ObjectInputStream(ios); //返回生成的新对象 cloneObj (T) ois.readObject(); } catch (Exception e) { e.printStackTrace(); } finally { //PS:ByteArrayOutputStream和ByteArrayInputStream其实是伪装成流的字节数组 // 把它们当成字节数据来看就好了 // 他们不会锁定任何文件句柄和端口如果不再被使用字节数组会被垃圾回收掉所以不需要关闭。 CloneUtils.close(obs); CloneUtils.close(ois); } return cloneObj; } /** * 关闭br * 关闭失败不会抛出异常 * * param closeable 被关闭的对象 */ public static void close(Closeable closeable) { if (null ! closeable) { try { closeable.close(); } catch (Exception e) { // 静默关闭 } } } }import java.io.Serializable; /** * author Evan Walker * version 1.0 * desc 深克隆测试类 昂焱数据: https://www.ayshuju.com * date 2023/04/10 14:25:26 */ public class Diary implements Serializable { private String author; private String content; private Diary diary; Override public String toString() { return Diary{ author author \ , content content \ , diary diary }; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author author; } public String getContent() { return content; } public void setContent(String content) { this.content content; } public Diary getDiary() { return diary; } public void setDiary(Diary diary) { this.diary diary; } public Diary() { } public Diary(String author, String content) { this.author author; this.content content; } public Diary(String author, String content, Diary diary) { this.author author; this.content content; this.diary diary; } public static void main(String[] args){ Diary diary new Diary(昂焱科技,深克隆测试); Diary diary1 new Diary(昂焱数据,深克隆测试,diary); Diary diary2 CloneUtils.deepClone(diary1); System.out.println(diary1.toString()); System.out.println(diary2.toString()); System.out.println(diary1.getDiary().hashCode() diary2.getDiary().hashCode()); System.out.println(diary1.hashCode() diary2.hashCode()); }所有属性都是一份拷贝, 跟原数据不会有任何耦合(不存在引用共享)原型模式简单实现方式简单实现方式包含三个角色:抽象原型Prototype:抽象原型类给出所有的具体原型所需要实现的拷贝方法的接口或者抽象类。具体原型ConcretePrototype:用于拷贝的原型实例实现了抽象原型Prototype所要求的接口。客户Client:客户端Client用来请求创建新的对象。抽象原型AbstratePrototype/** * author Evan Walker * version 1.0 * desc 形状抽象类 昂焱数据: https://www.ayshuju.com * date 2023/04/10 21:47:04 */ public abstract class Shape implements Cloneable { private String id; protected String type; abstract void describe(); public String getType(){ return type; } public String getId() { return id; } public void setId(String id) { this.id id; } Override public Object clone() { Object clone null; try { clone super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return clone; } Override public String toString() { return Shape{ id id \ , type type \ }; } }具体原型ConcretePrototype/** * author Evan Walker * version 1.0 * desc 形状原型具体实现类 昂焱数据: https://www.ayshuju.com * date 2023/04/10 21:49:49 */ public class Circle extends Shape { public Circle(){ type Circle; } Override void describe() { System.out.println(describe : Circle ); } } public class Rectangle extends Shape { public Rectangle(){ type Rectangle; } Override void describe() { System.out.println(describe : Rectangle); } } public class Square extends Shape { public Square(){ type Square; } Override void describe() { System.out.println(describe : Square); } }客户Client/** * author Evan Walker * version 1.0 * desc 原型测试 昂焱数据: https://www.ayshuju.com * date 2023/04/11 09:28:54 */ public class Client { public static void main(String[] args) { Square square new Square(); Square square1 (Square) square.clone(); System.out.println(square.toString() square.hashCode()); System.out.println(square1.toString() square1.hashCode()); Rectangle rectangle new Rectangle(); Rectangle rectangle1 (Rectangle) rectangle.clone(); System.out.println(rectangle.toString() rectangle.hashCode()); System.out.println(rectangle1.toString() rectangle1.hashCode()); Circle circle new Circle(); Circle circle1 (Circle) circle.clone(); System.out.println(circle.toString() circle.hashCode()); System.out.println(circle1.toString() circle1.hashCode()); } }原型模式登记实现方式简单实现方式包含三个角色:抽象原型Prototype抽象原型类给出所有的具体原型所需要实现的拷贝方法的接口或者抽象类。具体原型ConcretePrototype用于拷贝的原型实例实现了抽象原型Prototype所要求的接口。原型管理器PrototypeManager原型管理器PrototypeManager类中使用HashMap保存多个复制的原型对象供客户端Client类可以通过原型管理器中的get(String id) 方法获取复制的原型对象。客户Client客户端Client用来请求创建新的对象。抽象原型Prototype 同简单原型具体原型ConcretePrototype 同简单原型原型管理器PrototypeManagerimport java.util.concurrent.ConcurrentHashMap; /** * author Evan Walker * version 1.0 * desc 形状原型管理器 昂焱数据: https://www.ayshuju.com * date 2023/04/10 21:52:52 */ public class PrototypeManager { private static ConcurrentHashMapString, Shape shapeMap new ConcurrentHashMap(); /** * 根据形状id获取形状 * param shapeId 形状id * return */ public static Shape getShape(String shapeId) { Shape cachedShape shapeMap.get(shapeId); return (Shape) cachedShape.clone(); } /** * 对形状进行一个简单的初始化 * shapeMap.put(shapeKey, shape); */ public static void loadCache() { Circle circle new Circle(); circle.setId(1); shapeMap.put(circle.getId(), circle); Square square new Square(); square.setId(2); shapeMap.put(square.getId(), square); Rectangle rectangle new Rectangle(); rectangle.setId(3); shapeMap.put(rectangle.getId(), rectangle); } }客户Client/** * author Evan Walker * version 1.0 * desc 原型测试 昂焱数据: https://www.ayshuju.com * date 2023/04/10 21:51:32 */ public class ClientTest { public static void main(String[] args) { PrototypeManager.loadCache(); Shape clonedShape PrototypeManager.getShape(1); System.out.println(Shape : clonedShape.getType()); Shape clonedShape2 PrototypeManager.getShape(2); System.out.println(Shape : clonedShape2.getType()); Shape clonedShape3 PrototypeManager.getShape(3); System.out.println(Shape : clonedShape3.getType()); } }原型模式的特点特点原型模式属于创建型模式原型模式要求原型类实现拷贝自身的接口,通过拷贝原型的实例来创建新的对象原型模式拷贝原型实例创建的新对象不需要关心对象本身的类型,只需要实现原型实例拷贝自身的方法,通过自定义的拷贝方法来创建新的对象,不需要通过new创建新的对象。优点克隆对象 而无需与它们所属的具体类相耦合。克隆预生成原型 避免反复运行初始化代码。更方便地生成复杂对象。用继承以外的方式来处理复杂对象的不同配置。缺点克隆包含循环引用的复杂对象可能会非常麻烦。原型模式需要为每一个类配置一个clone()方法。clone()方法在每个类的内部,当对已有类进行重构时,需要修改代码,违背了开闭原则。应用场景当需要复制一些对象同时又希望代码独立于这些对象所属的具体类可以使用原型模式。当一个类初始化需要消耗很多资源时有很多相似对象时可以设计一个原型通过对成员变量的些微修改来实现。如果子类的区别仅在于其对象的初始化方式当创建这些子类的目的是为了创建特定类型的对象时可使用该模式来减少子类的数量。Spring中原型bean的创建就是原型模式的应用。可以用深克隆的方式保存对象的状态比如实现撤销操作等。