設計模式:13.原型模式及淺拷貝、深拷貝

1.克隆羊問題

(1) 問題

一隻羊,姓名爲Tom,年齡爲1,顏色爲白色。現在需要創建10只羊,他們的屬性和Tom的屬性完全相同。

(2) 傳統方法實現

在這裏插入圖片描述

/** 羊類 **/
public class Sheep{
	private String name;
	private int age;
	private String color;

	public Sheep(String name, int age, String color){
		super();
		this.name = name;
		this.age = age;
		this.color = color;
	}

	setter & getters
}

/** 創建羊 **/
public class Client(){
	psvm() {
		Sheep tom = new Sheep("Tom",1,"白色");
		
		Sheep sheep2 = new Sheep(tom.getName(), tom.getAge(), tom.getColor());
		Sheep sheep3 = new Sheep(tom.getName(), tom.getAge(), tom.getColor());
		
		...10}
}
(3) 優缺點
  • 優點:簡單好理解
  • 缺點:在創建新的對象時,總是需要重新獲取原始對象的屬性(tom.getXX()),如果創建的對象比較複雜時,效率較低。
  • 總是需要重新初始化對象,而不是動態獲取的對象運行時的狀態,不夠靈活。
(4) 解決方案

Java中Object類是所有類的根類,Object類提供了一個clone()方法,該方法可以將一個Java對象複製一份。
但是需要實現clone的Java類必須要實現一個接口Cloneable,該接口表示該類能夠複製且具有複製的能力。


2.原型模式(Prototype)

(1) 基本介紹
  • 用原型實例指定創建對象的種類,並且通過拷貝這些原型,創建新的對象。
  • 原型模式是一種創建型設計模式,允許一個對象在創建另一個可定製的對象,無需知道如何創建的細節。
  • 通過一個原型對象傳給那個要發動創建的對象,這個要發動創建的對象通過請求原型對象拷貝他們自己來實施創建。即對象.clone()
  • 原理:
    在這裏插入圖片描述
(2) 用原型模式實現克隆羊
/** 羊類 **/
public class Sheep implements Cloneable{
	private String name;
	private int age;
	private String color;

	public Sheep(String name, int age, String color){
		super();
		this.name = name;
		this.age = age;
		this.color = color;
	}

	// 克隆該實例
	@override
	protected Object clone() throws CloneNotSupportedException{
		Sheep sheep = null;
		sheep = (Sheep)super.clone();	// 使用默認的clone方法完成
		return sheep ;
	}

	setter & getters
}

/** 創建羊 **/
public class Client(){
	psvm() {
		Sheep tom = new Sheep("Tom",1,"白色");
		Sheep sheep2 = (Sheep)tom.clone();
		Sheep sheep3 = (Sheep)tom.clone();
	}
}
(3) 優缺點
  • 優點:這樣如果羊新增一個屬性,比如地區,只需要改動羊類的屬性和Tom的實例創建代碼即可。
  • 缺點:淺拷貝
    如果羊Sheep對象中有一個屬性:private Sheep friend; 使用默認的clone()這個拷貝前後這個屬性指向同一實例對象。

3.淺拷貝和深拷貝

(1) 淺拷貝
  • 對於數據類型是基本數據類型的成員變量,淺拷貝會直接進行值傳遞,也就是將該屬性值複製一份給新的對象。
  • 對於數據類型是引用數據類型的成員變量,比如說成員變量是某個數組、某個類的對象等,淺拷貝會進行引用傳遞:只是將該成員變量的引用值(內存地址)複製一份給新的對象。因爲實際上兩個對象的該成員變量都指向同一個實例。在拷貝前的對象中修改成員變量,會影響到拷貝後的對象中的該成員變量的值。
  • 默認的clone()方法是淺拷貝:sheep = (Sheep)super.clone();
(2) 深拷貝
  • 複製對象的所有基本數據類型的成員變量值。
  • 爲所有引用數據類型的成員變量申請存儲空間,並複製每個引用數據類型成員變量所引用的對象。
  • 實現方式:重寫clone()方法、通過對象序列化。

4.深拷貝方式1-重寫clone()

/** 鼠標類 **/
public class Mouse implements Cloneable{
	// 鼠標名稱(羅技、蘋果)
	private String name;
	// 接口類型(usb、type-c)
	private String usbType;

	public Mouse(String name, String usbType){
		this.name = name;
		this.usbType = usbType;
	}

	// 因爲鼠標類中的屬性都是基本數據類型,所以使用默認的clone()方法即可。
	@Override
	protected Object clone(){
		return super clone();
	}
}

/** 計算機類 **/
public class Computer implements Cloneable{
	// 電腦名稱(Macbook、聯想)
	public String name;
	// 鼠標
	public Mouse mouse;

	public Computer(){
	}

	// 深拷貝實現方式1:重寫clone()
	@Override
	protected Object clone(){
		Object deep = null;
		// 先使用默認的clone(), 完成對Computre類中的基本數據類型的拷貝
		deep  = super.clone();

		// 對Computer類中的引用數據類型進行處理
		Computer computer = (Computer)deep;
		computer.mouse = (Mouse)mouse.clone();
		
		return computer;
	}
}

5.深拷貝方式2-序列化(推薦使用)

/** 鼠標類 **/
public class Mouse implements Serializable{
	// 鼠標名稱(羅技、蘋果)
	private String name;
	// 接口類型(usb、type-c)
	private String usbType;
}

/** 計算機類 **/
public class Computer implements Serializable{
	// 電腦名稱(Macbook、聯想)
	public String name;
	// 鼠標
	public Mouse mouse;

	public Computer(){
	}

	// 深拷貝實現方式2:序列化
	public Object deepClone(){
		// 創建流對象
		ByteArrayOutputStream byteOut = null;
		ObjectOutputStream objectOut = null;
		ByteArrayInputStream byteIn = null;
		ObjectInputStream objectIn = null;
		
		// 序列化
		byteOut = new ByteArrayOutputStream();
		objectOut = new ObjectOutputStream(byteOut);
		objectOut.write(this);	// 把當前這個對象以對象流的方式輸出
		
		// 反列化
		byteIn = new ByteArrayInputStream(objectOut.toByteArray());
		objectIn = new ObjectInputStream(byteIn);
		Computer com = (Computer)objectIn.readObjct();
		
		// 流關閉,這裏應放在finally中
		byteOut.close();
		objectOut.close();
		byteIn.close();
		objectIn.close();
		
		return com;
	}
}

6.原型模式的注意事項

  • 創建新的對象比較複雜時,可以利用原型模式簡化對象的創建過程,同時也能提高效率。
  • 不用重新初始化對象,而是動態的獲得對象運行時的狀態。
  • 如果原始對象發生變化,如增加了2個屬性,則就不需要修改其他創建對象時的代碼,其他克隆對象就會發生相應的變化。
  • 在實現深克隆的時候,可能需要比較複雜的代碼。
  • 對已有的類要它支持克隆時,要修改該類的代碼,違背了ocp原則。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章