本文介绍
原型模式(Prototype Pattern)
原型模式
原型模式是一种创建型设计模式,原型模式(Prototype)允许一个对象再创建另外一个可定制的对象,
根本无需知道任何如何创建的细节
简介
用原型实例指定创建对象的种类,并且通过拷贝这些原型来创建新的对象
主要角色
在这样的设计模式中,有以下几个角色:
1、客户(Client)角色:客户端类向原型管理器提出创建对象的请求
2、抽象原型(Prototype)角色:这是一个抽象角色,通常由一个C#接口或抽象类实现。此角色给出所有的具体原型类所需的接口。在C#中,抽象原型角色通常实现了ICloneable接口。
3、具体原型(Concrete Prototype)角色:被复制的对象。此角色需要实现抽象的原型角色所要求的接口。
4、原型管理器(Prototype Manager)角色:创建具体原型类的对象,并记录每一个被创建的对象。
基本的实现思路
通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建
优缺点
优点
1、当创建的对象实例较为复杂的时候,使用原型模式可以简化对象的创建过程!
2、扩展性好,由于写原型模式的时候使用了抽象原型类,在客户端进行编程的时候可以将具体的原型类通过配置进行读取。
3、可以使用深度克隆来保存对象的状态,使用原型模式进行复制。当你需要恢复到某一时刻就直接跳到。比如我们的idea种就有历史版本,或则SVN中也有这样的操作。非常好用!
缺点
1、需要为每一个类配备一个克隆方法,而且该克隆方法位于一个类的里面,当对已有的类经行改造时需要修改源代码,违背了开闭原则。
2、在实现深克隆的时需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用的时候,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现相对麻烦。
适用场景
1创建对象成本比较大,比如初始化要很长时间的,占用太多CPU的,新对象可以通过复制已有的对象获得的,如果是相似的对象,则可以对其成员变量稍作修改。
2系统要保存对象状态的,而对象的状态改变很小。
3需要避免使用分层次的工厂类来创建分层次的对象,并且类的对象就只用一个或很少的组合状态!
注意事项
实现代码
1、创建一个实现了 Cloneable 接口的抽象类。
public abstract class Shape implements Cloneable {
private String id;
protected String type;
abstract void draw();
public String getType(){
return 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 (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
}
2、创建扩展了上面抽象类的实体类
public class Rectangle extends Shape {
public Rectangle(){
type = "Rectangle";
}
@Override
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
public class Square extends Shape {
public Square(){
type = "Square";
}
@Override
public void draw() {
System.out.println("Inside Square::draw() method.");
}
}
public class Circle extends Shape {
public Circle(){
type = "Circle";
}
@Override
public void draw() {
System.out.println("Inside Circle::draw() method.");
}
}
3、创建一个类,从数据库获取实体类,并把它们存储在一个 Hashtable 中
import java.util.Hashtable;
public class ShapeCache {
private static Hashtable<String, Shape> shapeMap
= new Hashtable<String, Shape>();
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);
}
}
4、PrototypePatternDemo 使用 ShapeCache 类来获取存储在 Hashtable 中的形状的克隆
public class PrototypePatternDemo {
public static void main(String[] args) {
ShapeCache.loadCache();
Shape clonedShape = (Shape) ShapeCache.getShape("1");
System.out.println("Shape : " + clonedShape.getType());
Shape clonedShape2 = (Shape) ShapeCache.getShape("2");
System.out.println("Shape : " + clonedShape2.getType());
Shape clonedShape3 = (Shape) ShapeCache.getShape("3");
System.out.println("Shape : " + clonedShape3.getType());
}
}
5、客户端类代码片段:
在客户端代码中,无须关心产品对象的具体组装过程,只需确定具体建造者的类型即可,建造者模式将复杂对象的构建与对象的表现分离开来,这样使得同样的构建过程可以创建出不同的表现。
package com.test.builder;
public class TestMain {
//something
Builder builder = new ConcreteBuilder();
Director director = new Director(builder);
Product product = director.construct();
//something
}
输出结果:
Shape : Circle
Shape : Square
Shape : Rectangle
总结
参考文章
https://baike.baidu.com/item/%E5%8E%9F%E5%9E%8B%E6%A8%A1%E5%BC%8F/4941014?fr=aladdin
https://blog.csdn.net/qq_40709468/article/details/82316418
http://www.runoob.com/design-pattern/prototype-pattern.html