zoukankan      html  css  js  c++  java
  • Java设计模式之原型模式

    概论

    什么是原型模式呢?用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。这个模式就叫作原型模式。原型模式属于对象创建者模式。

    原型模式示例

     首先我们需要有一个原型。这个原型实现了Cloneable空接口。这是一个标记接口,并无任何的方法。

     1 package com.example.pattern.prototype;
     2 
     3 import lombok.*;
     4 
     5 import java.io.Serializable;
     6 import java.util.Arrays;
     7 import java.util.List;
     8 
     9 @Setter
    10 @Getter
    11 @NoArgsConstructor
    12 @AllArgsConstructor
    13 public class PrototypeClass implements Cloneable {
    14 
    15     private int id;
    16     private char code;
    17     private String name;
    18 
    19     private BaseDomain baseDomain;
    20     private String[] array;
    21 
    22     private List<String> list;
    23 
    24 
    25     @Override
    26     protected PrototypeClass clone() {
    27         PrototypeClass prototypeClass = null;
    28 
    29         try {
    30             prototypeClass = (PrototypeClass) super.clone();
    31         } catch (CloneNotSupportedException e) {
    32             e.printStackTrace();
    33 
    34         }
    35 
    36         return prototypeClass;
    37     }
    38 
    39 
    40 }

    第9行-12行:采用lambok注解,简化了简单对象中的繁琐的get,set,带所有参数的构造函数,无参构造函数。

    第15-22行:定义了类型为原子类型,复杂对象,已经数组和集合的成员属性。

    最后,我们需要增加一个场景类Client:

     1 public class Client {
     2 
     3     public static void main(String[] args) {
     4         PrototypeClass prototypeClass = new PrototypeClass();
     5         prototypeClass.setId(123);
     6         prototypeClass.setCode('A');
     7         prototypeClass.setName("Tom");
     8         prototypeClass.setBaseDomain(new BaseDomain());
     9 
    10         String[] array = new String[]{"22222"};
    11         prototypeClass.setArray(array);
    12 
    13         List<String> list = new ArrayList<String>();
    14         list.add("CCC");
    15         prototypeClass.setList(list);
    16 
    17         PrototypeClass cloneClass = prototypeClass.clone();
    18 
    19         cloneClass.setId(456);
    20         cloneClass.setCode('B');
    21         cloneClass.setName("Jack");
    22 
    23         System.out.println("prototypeClass.getId() :"+prototypeClass.getId());
    24         System.out.println("cloneClass.getId():"+cloneClass.getId());
    25 
    26         System.out.println("prototypeClass.getCode() :"+prototypeClass.getCode());
    27         System.out.println("cloneClass.getCode():"+cloneClass.getCode());
    28 
    29         System.out.println("prototypeClass.getName() :"+prototypeClass.getName());
    30         System.out.println("cloneClass.getName():"+cloneClass.getName());
    31 
    32         String[] array2 = new String[]{"33333"};
    33         cloneClass.setArray(array);
    34 
    35         List<String> list2 = new ArrayList<String>();
    36         list.add("DDDD");
    37         cloneClass.setList(list);
    38 
    39 
    40         System.out.println("prototypeClass.getList().get(0) :"+prototypeClass.getList().get(0));
    41         System.out.println("cloneClass.getList().get(0) :"+cloneClass.getList().get(0));
    42 
    43         System.out.println("prototypeClass.getArray()[0] :"+prototypeClass.getArray()[0]);
    44         System.out.println("cloneClass.getArray()[0] :"+cloneClass.getArray()[0]);
    45 
    46 
    47 
    48     }
    49 }

    第17行:调用了对象的clone方法,直接产生一个对象。这就是对象的复制,而不是使用new 指令。使用对象复制的方式,不会调用构造函数。

    我们先执行一下打印出来的结果:

     1 prototypeClass.getId() :123
     2 cloneClass.getId():456
     3 prototypeClass.getCode() :A
     4 cloneClass.getCode():B
     5 prototypeClass.getName() :Tom
     6 cloneClass.getName():Jack
     7 prototypeClass.getList().get(0) :CCC
     8 cloneClass.getList().get(0) :CCC
     9 prototypeClass.getArray()[0] :22222
    10 cloneClass.getArray()[0] :22222

     从以上执行结果来看

    ①:如果成员属性为int char String类型,复制后的对象的属性的改变不会对原始对象的属性产生任何的影响。

    ②:如果成员属为是List集合,数组,复制后的对象的属性的改变也会和原始对象的属性产生了影响。

    这是为什么呢?因为我们在原型中的拷贝方式是浅拷贝。什么是浅拷贝呢?super.clone是谁的方法呢,当然是Object方法的,因为Object是任何类的超类。而Object类提供的clone方法只是拷贝本对象,这个对象的内部成员属性包括数组,集合,引用对象都不拷贝,还是执行原生对象的内部元素地址。因此数组,集合,引用对象都是在原生对象还是拷贝对象中都是共享而存在的。这就是浅拷贝。

    浅拷贝的对象中的成员属性还有对象的情况下, 像以上例子中的 BaseDomain。改变了同一个BaseDomian实例的属性name的情况下, 因为是同一个实例,因此也是共享的,一边都变。如果是重新new一个BaseDomain实例,重新对拷贝之后的对象复制,那是互相不干扰的。 

    原型模式在源码中的应用

     1 public Object clone() {
     2         try {
     3             ArrayList<?> v = (ArrayList<?>) super.clone();
     4             v.elementData = Arrays.copyOf(elementData, size);
     5             v.modCount = 0;
     6             return v;
     7         } catch (CloneNotSupportedException e) {
     8             // this shouldn't happen, since we are Cloneable
     9             throw new InternalError(e);
    10         }
    11     }

    以上是ArrayList的clone方法,我们可以产生了一个list之后,来clone一下,来简化操作,这里用到的是深拷贝。为什么是深拷贝而不是浅拷贝呢?第4-行-5行数组,修改次数再复制,拷贝之后的对象与原来的对象不再持有同一份引用,因此是深拷贝。而且Arrays.copyOf方法中是重新创建的一个新数组。

  • 相关阅读:
    linux 文件权限(s、t、i、a)解析
    vim Vundle
    数据结构学习(1)
    Android ImageView设置图片原理(上)
    C++11 之auto
    Android屏幕分辨率获取方法--源码剖析
    C++的发展方向是对的嘛?
    c++ 的前世今生
    学习知识的一种思路
    遗失的访谈小评
  • 原文地址:https://www.cnblogs.com/sunshine798798/p/10057365.html
Copyright © 2011-2022 走看看