DesignPattern - 原型模式【建立型】

歡迎關注微信公衆號:FSA全棧行動 👋java

1、原型模式介紹

原型模型(Prototype) 是一種對象建立型模式,使用原型實例指定建立對象的種類,並經過拷貝這些原型建立新的對象,主要用於建立重複的對象,同時又能保證性能。工做原理是將一個原型對象傳給那個要發動建立的對象,這個要發動建立的對象經過原型對象拷貝本身來實現建立過程。原型模型應該是最簡單的設計模式了,實現一個接口,重寫一個方法便可完成原型模式。設計模式

  • 核心組成
    • Prototype:聲明克隆方法的接口,是全部具體原型類的公共父類,也就是 Cloneable 接口
    • ConretePrototype:具體原型類,也就是實現了 Cloneable 接口的類
    • Client:讓一個原型對象克隆自身從而建立一個新的對象,也就是使用者(好比在 main 函數中)
  • 應用場景
    • 建立新對象成本較大,新的對象能夠經過原型模式對已有對象進行復制來獲取
    • 若是系統要保存對象的狀態,作備份使用
  • 兩種拷貝
    • 㳀拷貝:若是原型對象的成員變量是基本數據類型(int、double、byte、boolean、char 等),將複製一份給克隆對象;若是原型對象的成員變量是引用類型,則將引用對象的地址複製一份給克隆對象,也就是說原型對象和克隆對象的成員變量指向相同的內存地址。經過覆蓋 Object 類的 clone()方法能夠實現淺克隆。
    • 深拷貝:不管原型對象的成員變量是基本數據類型仍是引用類型,都將複製一份給克隆對象,若是須要實現深克隆,能夠經過序列化(Serializable)等方式來實現。
  • 優勢
    • 當建立新的對象實例較爲複雜時,使用原型模式能夠簡化對象的建立過程,能夠提升實例的建立效率
    • 可輔助實現撤銷操做,使用深克隆的方式保存對象的狀態,使用原型模式將對象複製一份並將其狀態保存起來,以便在須要的時候使用恢復到歷史狀態
  • 缺點
    • 須要爲每個類配備一個克隆方法,對已有類進行改造時,須要修改源代碼,違背了「開閉原則」。
    • 在實現深克隆時須要編寫較爲複雜的代碼,且當對象之間存在多重嵌套引用時,須要對每一層對象對應的類都必須支持深克隆。

綜上,爲方便記憶原型模式,能夠 「膚淺」 的認爲:微信

  • 淺拷貝:實現 Cloneable 接口
  • 深拷貝:實現 Serializable 接口 或 藉助其餘序列化手段

如下,是對 Cloneable 接口的補充說明:markdown

  • Cloneable 接口是一個空接口,僅用於標記對象,Cloneable 接口裏面是沒有 clone()方法的,clone()方法是 Object 類裏面的方法,默認實現是一個 Native 方法。
  • 若是對象 implement Cloneable 接口的話,須要覆蓋 clone 方法(由於 Object 類的 clone 方法是 protected,須要覆蓋爲 public)

2、原型模式代碼實現

建立原型類(ConretePrototype) :app

public class Person implements Cloneable, Serializable {

	private String name;
	private int age;
	private List<String> list;

	public String getName() {
		return name;
	}

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

	public int getAge() {
		return age;
	}

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

	public List<String> getList() {
		return list;
	}

	public void setList(List<String> list) {
		this.list = list;
	}

	@Override
	protected Person clone() throws CloneNotSupportedException {
		return (Person) super.clone();
	}

	public Person deepClone() {
		Person copyObj = null;
		byte[] byteArray = null;

		// 序列化 輸出
		try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
				ObjectOutputStream oos = new ObjectOutputStream(baos);) {
			oos.writeObject(this);
			byteArray = baos.toByteArray();
		} catch (Exception e) {
			e.printStackTrace();
		}

		// 反序列化 輸入
		try (ByteArrayInputStream bais = new ByteArrayInputStream(byteArray);
				ObjectInputStream ois = new ObjectInputStream(bais);) {
			copyObj = (Person) ois.readObject();
		} catch (Exception e) {
			e.printStackTrace();
		}

		return copyObj;
	}
}
複製代碼

注意:deepClone()中 try-catch 的用法是 JDK1.7 優化後的 try-with-resource 語法,會自動調用 close()。ide

使用方使用原型類克隆出新對象實例:函數

public class Client {
	public static void main(String[] args) throws CloneNotSupportedException {
		List<String> fruits = new ArrayList<String>(Arrays.asList("apple", "banana", "pear"));

		Person person1 = new Person();
		person1.setName("GitLqr");
		person1.setAge(18);
		person1.setList(fruits);

		// 淺拷貝
		// Person person2 = person1.clone();
		// 深拷貝
		Person person2 = person1.deepClone();
		person2.setName("CharyLin");
		person2.getList().add("shit");

		System.out.println("person1 name is " + person1.getName() + ", age is " + person1.getAge() + ", list is "
				+ person1.getList());
		System.out.println("person2 name is " + person2.getName() + ", age is " + person2.getAge() + ", list is "
				+ person2.getList());
	}
}
複製代碼

若是文章對您有所幫助, 請不吝點擊關注一下個人微信公衆號:FSA全棧行動, 這將是對我最大的激勵. 公衆號不只有Android技術, 還有iOS, Python等文章, 可能有你想要了解的技能知識點哦~oop

相關文章
相關標籤/搜索