原型模式
什么是原型模式?
- 原型模式是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
实现原理
- 这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如:一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。
适用场景
- 资源优化场景
- 类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。
- 性能和安全要求的场景
- 通过new产生一个对象需要非常频繁的数据准备或访问权限,则可以使用原型模式。
- 一个对象多个修改者的场景。
- 一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用
- 在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过 clone 的方法创建一个对象,然后由工厂方法提供给调用者。原型模式已经与 Java 融为浑然一体,大家可以随手拿来使用。
特点
- 效率高,直接克隆,避免了重新执行构造过程步骤。
- 克隆出的对象其属性与原型对象相同,克隆出的对象不会改变原型对象。
浅克隆存在的问题
被赋值的对象对其他对象的引用仍指向原来的对象。
深克隆的优势
- 把引用类的对象也进行克隆,不再指向原来的对象,解决的浅克隆的问题。
- 深克隆让已实现cloneable接口的类中的属性也实现cloneable接口。
- 深度克隆的类内部基本数据类型和String能够自动实现深度克隆。
借用一下大佬的图:
浅拷贝实现 Cloneable,重写,深拷贝是通过实现 Serializable 读取二进制流。
浅克隆的实现
package prototype;
//所有图像类的基类
//实现Cloneable接口使用克隆功能
public abstract class shape implements Cloneable{
private String id;
protected String type;
abstract void draw();
public String getType() {
return this.type;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id=id;
}
public Object clone() {
Object clone=null;
try {
clone=super.clone();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
return clone;
}
}
package prototype;
//长方形类
public class Rectangle extends shape{
public Rectangle() {
type="Rectangle";
}
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
package prototype;
//正方形类
public class Square extends shape {
public Square() {
// TODO Auto-generated constructor stub
type="Square";
}
@Override
void draw() {
// TODO Auto-generated method stub
System.out.println("Inside Square::draw() method");
}
}
package prototype;
import java.util.Hashtable;
public class ShapeCache {
//保存类的原型
private static Hashtable<String, shape> shapMap=new Hashtable<String,shape>();
//产生原型类的浅克隆对象
public static shape getShape(String shapeId) {
shape cachedShape=shapMap.get(shapeId);
return (shape)cachedShape.clone();
}
//加载所有原型类存入Hashtable中
public static void loadCache() {
Square square=new Square();
square.setId("2");
shapMap.put(square.getId(), square);
Rectangle rectangle=new Rectangle();
rectangle.setId("3");
shapMap.put(rectangle.getId(), rectangle);
}
}
package prototype;
public class PrototypePatternDemo {
public static void main(String[] args) {
ShapeCache.loadCache();
shape cloneShape=(shape)ShapeCache.getShape("2");
System.out.println("shape:"+cloneShape.getType());
}
}
深克隆的实现
使用序列化与反序列化的技巧,实现方式如下:
给shape类实现Serializable
//产生原型类的深克隆对象
public static shape getShape2(String shapeId) throws IOException, ClassNotFoundException
{
shape cachedShape=shapMap.get(shapeId);
//创建字节输出流
ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream();
//创建对象输出流
ObjectOutputStream objectOutputStream=new ObjectOutputStream(byteArrayOutputStream);
//将原型对象写入对象输出流中,因为对象输出流定向输出到字节输出流,生成的流会保存在字节输出流中
objectOutputStream.writeObject(cachedShape);
//转为二进制流
byte[] bytes=byteArrayOutputStream.toByteArray();
ByteArrayInputStream bInputStream=new ByteArrayInputStream(bytes);
//将二进制流转对象
ObjectInputStream oInputStream=new ObjectInputStream(bInputStream);
shape cachedShape2=(shape)oInputStream.readObject();
return cachedShape2;
}
开发中的应用场景
原型模式很少单独出现,一般是和工厂方法模式一起出现,通过clone的方法创建一个对象,然后由工厂方法提供给调用者。
Spring中的Bean的创建实际就是两种:单例模式和原型模式(原型模式需要和工厂模式搭配起来)