天天看點

設計模式之路 | 原型模式

原型模式的定義與特點

原型(Prototype)模式的定義如下:用一個已經建立的執行個體作為原型,通過複制該原型對象來建立一個和原型相同或相似的新對象。在這裡,原型執行個體指定了要建立的對象的種類。用這種方式建立對象非常高效,根本無須知道對象建立的細節。例如,Windows 作業系統的安裝通常較耗時,如果複制就快了很多。在生活中複制的例子非常多,這裡不一一列舉了。

原型模式的結構與實作

由于 Java 提供了對象的 clone() 方法,是以用 Java 實作原型模式很簡單。

1. 模式的結構

原型模式包含以下主要角色。

  1. 抽象原型類:規定了具體原型對象必須實作的接口。
  2. 具體原型類:實作抽象原型類的 clone() 方法,它是可被複制的對象。
  3. 通路類:使用具體原型類中的 clone() 方法來複制新的對象。
設計模式之路 | 原型模式
public class Sheep implements Cloneable {

  private String name;
  private Integer age;
  private String color;
  
  private Sheep friend;

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public Integer getAge() {
    return age;
  }

  public void setAge(Integer age) {
    this.age = age;
  }

  public String getColor() {
    return color;
  }

  public void setColor(String color) {
    this.color = color;
  }
  
  public Sheep getFriend() {
    return friend;
  }

  public void setFriend(Sheep friend) {
    this.friend = friend;
  }

  @Override
  protected Object clone() {
    Sheep s = null;
    try {
      s = (Sheep) super.clone();
    } catch (CloneNotSupportedException e) {
      e.printStackTrace();
    }
    return s;
  }

  @Override
  public String toString() {
    return "Sheep [name=" + name + ", age=" + age + ", color=" + color + ", friend=" + friend.hashCode() + "]";
  }
}      
public class PrototypeDemo {

  public static void main(String[] args) {
    Sheep sheep1 = new Sheep();
    sheep1.setName("sheep01");
    sheep1.setAge(10);
    sheep1.setColor("黃色");
    
    Sheep sheep2 = new Sheep();
    sheep2.setName("sheep02");
    sheep2.setAge(15);
    sheep2.setColor("白色");
    sheep2.setFriend(sheep1);
    
    Object clone = sheep2.clone();
    
    System.out.println(sheep2);
    System.out.println(clone);
  }
}      
設計模式之路 | 原型模式

深拷貝

有上述輸入的值可以看出,拷貝後的 friend 對象的hashCode 相等,是以friend是同一個對象。是以,可以确定通過 super.clone() 的對象是錢拷貝的對象。

淺拷貝介紹

1. 對于資料類型是基本資料類型的成員變量,淺拷貝會直接進行值傳遞,也就是将該屬性值複制一份給新的的對象

2. 對于資料類型是引用資料類型的成員變量,比如說成員變量是某個數組、某個類的對象等,那麼淺拷貝會進行引用傳遞,也就是将該成員變量的引用值(記憶體位址)複制一份給新的對象。因為實際上兩個對象的成員變量都指向同一個執行個體。在這種情況下,在一個對象中修改該成員變量會影響到另一個對象的該成員變量值。

深拷貝介紹

1. 複制對象的所有基本資料類型的成員變量值

2. 為所有引用資料類型的成員變量申請存儲空間,并複制每個引用資料類型成員變量所引用的對象,直到該對象可達的所有對象。也就是說,對象進行深拷貝要對整個對象進行拷貝

3. 深拷貝實作方式1:重寫clone方法來實作深拷貝

4. 深拷貝實作方式2:通過對象序列化來實作深拷貝

​第一種方式:重寫clone方法來實作深拷貝​

public class Sheep implements Cloneable, Serializable {

  private static final long serialVersionUID = 2795877779132802842L;
  
  private String name;
  private Integer age;
  private String color;
  
  private Sheep friend;

        // 省略 getter 和 setter 方法

  @Override
  protected Object clone() {
    Sheep s = null;
    try {
      s = (Sheep) super.clone();
      if (s.getFriend() != null) {
        Sheep friend = (Sheep) s.getFriend().clone();
        s.friend = friend;
      }
    } catch (CloneNotSupportedException e) {
      e.printStackTrace();
    }
    return s;
  }

  @Override
  public String toString() {
    return "Sheep [name=" + name + ", age=" + age + ", color=" + color + ", friend=" + friend.hashCode() + "]";
  }
}      

​第二種方式:通過對象序列化來實作深拷貝​

public class Sheep implements Cloneable, Serializable {

  private static final long serialVersionUID = 2795877779132802842L;
  
  private String name;
  private Integer age;
  private String color;
  
  private Sheep friend;

        // 此處省略 getter 和 setter

  @Override
  protected Object clone() {
    ByteArrayOutputStream baos = null;
    ObjectOutputStream oos = null;
    ByteArrayInputStream bais = null;
    ObjectInputStream ois = null;
    Sheep s = null;
    try {
      // 序列化
      baos = new ByteArrayOutputStream();
      oos = new ObjectOutputStream(baos);
      // 目前這個對象以對象流的方式輸出
      oos.writeObject(this);
      
      // 反序列化
      bais = new ByteArrayInputStream(baos.toByteArray());
      ois = new ObjectInputStream(bais);
      s = (Sheep) ois.readObject();
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      // close流,此處省略
    }
    return s;
  }

  @Override
  public String toString() {
    return "Sheep [name=" + name + ", age=" + age + ", color=" + color + ", friend=" + friend.hashCode() + "]";
  }
}      

原型模式的注意事項和細節

1. 建立新的對象比較複雜時,可以利用原型模式簡化對象的建立過程,同時也能夠提高效率

2. 不用重新初始化新的對象,而是動态地獲得對象運作時的狀态

繼續閱讀