目录
什么是原型模式
原型模式(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 :同简单原型
原型管理器PrototypeManager
import 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 ConcurrentHashMap<String, 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的创建,就是原型模式的应用。
- 可以用深克隆的方式保存对象的状态,比如实现撤销操作等。
更多消息资讯,请访问昂焱数据(https://www.ayshuju.com)