欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

简说设计模式——原型模式

程序员文章站 2022-06-30 14:52:24
一、什么是原型模式 还记不记得初高中学生物的时候,细胞分裂时怎么实现的,一个细胞无论是细胞核分裂还是细胞质分裂,是不是都是通过克隆自身实现的。或者说我们去复印资料的时候,是不是直接对原本的资料进行复印,得到了一个一模一样的资料,这些都可以说是原型模式,下面看一下定义。 原型模式(Prototype) ......

一、什么是原型模式

  还记不记得初高中学生物的时候,细胞分裂时怎么实现的,一个细胞无论是细胞核分裂还是细胞质分裂,是不是都是通过克隆自身实现的。或者说我们去复印资料的时候,是不是直接对原本的资料进行复印,得到了一个一模一样的资料,这些都可以说是原型模式,下面看一下定义。

       原型模式(Prototype),用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。UML结构图如下:

简说设计模式——原型模式

       其中,Prototype为原型类,声明一个克隆自身的接口;ConcretePrototype为具体实现类,实现一个克隆自身的操作;而客户端Client只需让一个原型克隆自身,从而创建一个新的对象。

    1. Prototype

 1 public abstract class Prototype implements Cloneable {
 2     
 3     private String id;
 4     
 5     public Prototype(String id) {
 6         this.id = id;
 7     }
 8     
 9     public String getId() {
10         return id;
11     }
12 
13     @Override
14     public Prototype clone() {
15         Prototype prototype = null;
16         
17         try {
18             prototype = (Prototype) super.clone();
19         } catch (CloneNotSupportedException e) {
20             e.printStackTrace();
21         }
22         
23         return prototype;
24     }
25     
26 }

    2. ConcretePrototype

       创建当前对象的浅表副本。

1 public class ConcretePrototype extends Prototype {
2 
3     public ConcretePrototype(String id) {
4         super(id);
5     }
6     
7 }

    3. Client

1 public class Client {
2 
3     public static void main(String[] args) {
4         ConcretePrototype p1 = new ConcretePrototype("Hello");
5         ConcretePrototype c1 = (ConcretePrototype) p1.clone();
6         System.out.println(c1.getId());
7     }
8     
9 }

       运行结果为“Hello”,此时ConcretePrototype的对象p1得到了新的实例c1。

二、原型模式的应用

    1. 何时使用

  • 当一个系统应该独立于它的产品创建、构成和表示时。
  • 当要实例化的类是在运行时刻指定时(如动态装载)。
  • 为了避免创建一个与产品类层次平行的工厂类层次时。
  • 当一个类的实例只能有几种几个不同状态组合中的一种时。

    2. 优点

  • 性能优良。不用重新初始化对象,而是动态地获取对象运行时的状态。
  • 逃避构造函数的约束。

    3. 缺点

  • 配置克隆方法需要对类的功能进行通盘考虑。
  • 必须实现Cloneable接口。

    4. 使用场景

  • 资源优化场景。
  • 性能和安全要求的场景。
  • 一个对象多个修改者的场景。
  • 一般与工厂方法模式一起出现,通过clone方法创建一个对象,然后由工厂方法提供给调用者。

    5. 应用实例

  • 细胞分裂
  • Java中的Object.clone()方法
  • 复印

三、原型模式的实现

       下面我们创建一个抽象类和扩展了它的实体类,即图形类与圆形、矩形、三角形。之后再通过一个cache类将对象存储在一个Hashtable中,并在请求的时候返回它们的克隆。UML图如下:

简说设计模式——原型模式

    1. Shape类

       创建一个实现了Cloneable接口的抽象类。

 1 public abstract class Shape implements Cloneable {
 2     
 3     private String id;
 4     protected String type;
 5     
 6     public abstract void draw();
 7 
 8     public String getId() {
 9         return id;
10     }
11 
12     public void setId(String id) {
13         this.id = id;
14     }
15 
16     public String getType() {
17         return type;
18     }
19     
20     @Override
21     public Shape clone() {
22         Shape prototype = null;
23         
24         try {
25             prototype = (Shape) super.clone();
26         } catch (CloneNotSupportedException e) {
27             e.printStackTrace();
28         }
29         
30         return prototype;
31     }
32     
33 }

    2. 实现类

       创建扩展了上面抽象类的实现类,这里以Circle为例。

 1 public class Circle extends Shape {
 2 
 3     public Circle() {
 4         type = "圆形";
 5     }
 6     
 7     @Override
 8     public void draw() {
 9         System.out.println("圆形类的draw方法");
10     }
11 
12 }

    3. ShapeCache

       获取实体类,并存于Hashtable中。

 1 public class ShapeCache {
 2 
 3     private static Hashtable<String, Shape> shapeMap = new Hashtable<>();
 4     
 5     public static Shape getShape(String shapeId) {
 6         Shape shape = shapeMap.get(shapeId);
 7         
 8         return shape.clone();
 9     }
10     
11     //添加三种图形
12     public static void loadCache() {
13         Circle circle = new Circle();
14         circle.setId("1");
15         shapeMap.put(circle.getId(), circle);
16         
17         Triangle triangle = new Triangle();
18         triangle.setId("2");
19         shapeMap.put(triangle.getId(), triangle);
20         
21         Rectangle rectangle = new Rectangle();
22         rectangle.setId("3");
23         shapeMap.put(rectangle.getId(), rectangle);
24     }
25     
26 }

    4. Client客户端

 1 public class Client {
 2 
 3     public static void main(String[] args) {
 4         ShapeCache.loadCache();
 5         
 6         Shape clonedShape = ShapeCache.getShape("1");
 7         System.out.println("图形:" + clonedShape.getType());
 8         
 9         Shape clonedShape2 = ShapeCache.getShape("2");
10         System.out.println("图形:" + clonedShape2.getType());
11         
12         Shape clonedShape3 = ShapeCache.getShape("3");
13         System.out.println("图形:" + clonedShape3.getType());
14         
15     }
16     
17 }

       运行结果如下:

简说设计模式——原型模式

四、浅复制与深复制

       在上述代码中,对象里的数据都是String类型的,而String是一种拥有值类型特点的特殊引用类型,在使用clone()方法时,原始对象及其复本引用同一对象。也就是说,如果类中有对象引用,那么引用的对象数据时不会被克隆过来的。这就叫做“浅复制”。

       “浅复制”指,被复制的对象的所有变量都含有与原来的对象相同的值,而所有的其他对象的引用都仍然指向原来的对象。

       但我们可能更需要这样一种需求,把要赋值的对象所引用的对象都复制一遍。这种方式就是“深复制”。方法是对私有变量进行独立的复制。

       “深复制”指,把引用对象的变量指向复制过的新对象,而不是原有的被引用的对象。

       下面看一个例子。

    1. Client客户端

       先将客户端内容放上,深复制与浅复制只需修改注释即可。

 1 public class TestClient {
 2 
 3     public static void main(String[] args) {
 4         //产生对象
 5 //        ShallowCopy copy = new ShallowCopy();
 6         DeepCopy copy = new DeepCopy();
 7         copy.setValue("张三");
 8         
 9         //拷贝对象
10 //        ShallowCopy cloneCopy = copy.clone();
11         DeepCopy cloneCopy = copy.clone();
12         cloneCopy.setValue("李四");
13         
14         System.out.println(copy.getvalue());
15     }
16     
17 }

    2. 浅复制

 1 public class ShallowCopy implements Cloneable {
 2 
 3     private ArrayList<String> arrayList = new ArrayList<String>();
 4     
 5     @Override
 6     public ShallowCopy clone() {
 7         ShallowCopy sCopy = null;
 8         
 9         try {
10             sCopy = (ShallowCopy) super.clone();
11         } catch (CloneNotSupportedException e) {
12             // TODO Auto-generated catch block
13             e.printStackTrace();
14         }
15         
16         return sCopy;
17     }
18     
19     //set
20     public void setValue(String value) {
21         this.arrayList.add(value);
22     }
23     
24     //get
25     public ArrayList<String> getvalue() {
26         return this.arrayList;
27     }
28 }

       在客户端中运行结果如下:

简说设计模式——原型模式

       这里之所以出现了“李四”,是因为Java做了一个偷懒的复制动作,Object类提供的方法clone只是复制本对象,其对象内部的数组、引用对象等都不复制,还是指向原先对象的内部元素地址,这种复制就叫浅复制。两个对象共享了一个私有变量,是一种非常不安全的方式,大家都可以进行更改。

    3. 深复制

 1 public class DeepCopy implements Cloneable {
 2 
 3     private ArrayList<String> arrayList = new ArrayList<String>();
 4     
 5     @Override
 6     public DeepCopy clone() {
 7         DeepCopy dCopy = null;
 8         
 9         try {
10             dCopy = (DeepCopy) super.clone();
11             //增加这行
12             dCopy.arrayList = (ArrayList<String>) this.arrayList.clone();
13         } catch (CloneNotSupportedException e) {
14             // TODO Auto-generated catch block
15             e.printStackTrace();
16         }
17         
18         return dCopy;
19     }
20 
21     //set
22     public void setValue(String value) {
23         this.arrayList.add(value);
24     }
25     
26     //get
27     public ArrayList<String> getvalue() {
28         return this.arrayList;
29     }
30     
31 }

       在客户端中运行结果如下:

简说设计模式——原型模式

       仅仅增加了注释下的那一行代码,对私有的类变量进行独立的复制,这样就完成了完全的复制,两个对象间没有任何联系,各自修改互不影响,这就叫深复制。深复制还有一种实现方式是通过自己写二进制流来操作对象,然后实现对象的深复制。

       

       源码地址:https://gitee.com/adamjiangwh/GoF