設計模式之路 | 原型模式

原型模式的定義與特點

原型(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. 不用重新初始化新的對象,而是動態地獲得對象運行時的狀態

3.  jdk的克隆值淺拷貝,需要深拷貝則需要自己寫代碼實行

4. 缺點:需要爲每一個類都提供一個克隆的方法,這對全新的類來說並不困難,但是對已有的類進行修改時,需要需求其他的代碼,違背了ocp原則。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章