【創建型設計模式】原型模式

前言(一些廢話,可以忽略)

  • 同樣是創建型的設計模式,這種模式與工廠模式相比,結構更爲簡單,也更好理解,直接進入正題
  • PS.部分類實現見文末

要解決的問題

  • 克隆羊多利大家都知道,原型模式要解決的就是對象的複製問題,在沒有原型模式的情況下,我們直接通過複製對象的屬性值來獲取新的相同對象
/**
 * 羊
 * @program: ade-someproblem
 * @author: cade franklin
 * @create: 2019-12-22 22:12
 **/
public class Sheep {
    private String name;
    private Integer age;

    public Sheep(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
	//...toString
	//...setter
	//...getter
}
  • 在調用的時候直接通過獲取對象屬性,或設置相同的屬性值來進行克隆,顯然很原始,一旦原始對象發生變化,我們需要修改克隆使用的地方
/**
 * @program: ade-someproblem
 * @author: cade franklin
 * @create: 2019-12-22 22:12
 **/
public class BaseClone {

    public static void main(String[] args) {
        Sheep sheep1 = new Sheep("Sheep1", 1);
        //獲取原對象屬性
        Sheep sheep2 = new Sheep(sheep1.getName(), sheep1.getAge());
        //設置與原對象屬性相同的值
        Sheep sheep3 = new Sheep("Sheep1", 1);
        System.out.println(sheep1);
        System.out.println(sheep2);
        System.out.println(sheep3);

        System.out.println(sheep1.hashCode());
        System.out.println(sheep2.hashCode());
        System.out.println(sheep3.hashCode());
    }
}

淺拷貝原型模式

  • “淺拷貝原型模式”,這個名字是我自己取的,如果雷同是巧合,反正原型模式的主要目的是對象的複製(克隆),與上一篇一樣,我們同樣不需要在意這些細節
  • 淺拷貝,直接使用Object的clone方法,一個本地的複製方法,快速!高效!
  • 實現一個CopyPrototype接口,自己寫一個clone方法的接口方便深拷貝時重寫clone方法,也可以直接實現Cloneable方法,然後調用父類clone實現拷貝
/**
 * 可以克隆的羊
 * @program: ade-someproblem
 * @author: cade franklin
 * @create: 2019-12-22 22:12
 **/
public class Sheep implements Cloneable, CopyPrototype{
    private String name;
    private Integer age;

    @Override
    public CopyPrototype copyOnSelf() throws CloneNotSupportedException {
        return (CopyPrototype) this.clone();
    }
    
    //@Override
    //protected Object clone() throws CloneNotSupportedException {
    //	return super.clone();
	//
    //}
}
  • 克隆方法的時候,直接調用接口的複製自我方法copyOnSelf
/**
 * @program: ade-someproblem
 * @author: cade franklin
 * @create: 2019-12-22 22:12
 **/
public class ShallowPrototype {

    public static void main(String[] args) throws CloneNotSupportedException {
        Sheep sheep1 = new Sheep("sheep1", 2);
        Sheep sheep2 = (Sheep) sheep1.copyOnSelf();
		//值一樣
        System.out.println(sheep1);
        System.out.println(sheep2);
		//false,說明對象已經被複制
        System.out.println(sheep1.hashCode() == sheep2.hashCode());
		
    }
}

使用clone的深拷貝

  • 存在一個問題,當我們的sheep還有一個引用對象作爲成員變量的時候,如下

/**
 * 羊
 * @program: ade-someproblem
 * @author: cade franklin
 * @create: 2019-12-22 22:12
 **/
public class Sheep implements Cloneable, CopyPrototype {
    private String name;
    private Integer age;

    private SheepProperties properties;

    @Override
    public CopyPrototype copyOnSelf() throws CloneNotSupportedException {
        Sheep sheep =  (Sheep) this.clone();
        
        return sheep;
    }
}

public static void main(String[] args) throws CloneNotSupportedException {
    Sheep sheep1 = new Sheep("sheep1", 2);
    SheepProperties properties = new SheepProperties("中國", "山羊");
    sheep1.setProperties(properties);
    Sheep sheep2 = (Sheep) sheep1.copyOnSelf();

    System.out.println(sheep1);
    System.out.println(sheep2);

    System.out.println(sheep1.hashCode());
    System.out.println(sheep2.hashCode());

    System.out.println(sheep1.getProperties().hashCode());
    System.out.println(sheep2.getProperties().hashCode());
}
  • 這時我們會發現,sheep1和sheep2中的屬性properties的hashCode(如果沒有重寫,則是經由內存地址計算得出)會是一樣的,這說明我們在克隆的時候沒有複製引用對象,這就是一個問題,所以如果有引用對象,則需要將引用對象也實現Cloneable接口(我們自己的接口CopyPrototype可以不實現,與之前的邏輯一樣)
    @Override
    public CopyPrototype copyOnSelf() throws CloneNotSupportedException {
        Sheep sheep =  (Sheep) this.clone();
       	//深拷貝
		sheep.setProperties((SheepProperties) properties.copyOnSelf());
        return sheep;
    }
class SheepProperties implements Cloneable, CopyPrototype {
    private String placeOfProduction;
    private String breeding;

    @Override
    public CopyPrototype copyOnSelf() throws CloneNotSupportedException {
        return (CopyPrototype) this.clone();
    }
}
  • 存在一個問題,如果我們需要再在羊Sheep類中增加新的屬性呢,那麼就需要在重寫copyOnSelf方法,這就違反了ocp原則,如果我們的SheepProperties類中也要增加新的引用對象呢,又得重寫,這樣就無窮盡了,於是乎

基於序列化的深拷貝

  • 我們使用流的方式將對象序列化,這樣即使再對類做修改,或引用對象中還有引用對象,都無需去修改copyOnSelf方法,這樣擴展起來就比較好了
/**
 * 羊
 * @program: ade-someproblem
 * @author: cade franklin
 * @create: 2019-12-22 22:12
 **/
public class Sheep implements Cloneable, CopyPrototype, Serializable {
    private String name;
    private Integer age;

    private SheepProperties properties;
	//...省略一些方法
    @Override
    public CopyPrototype copyOnSelf() throws Exception{

        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(out);
        objectOutputStream.writeObject(this);

        ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
        ObjectInputStream objectInputStream = new ObjectInputStream(in);
        Sheep sheep = (Sheep) objectInputStream.readObject();
        return sheep;
    }
}
  • 屬性SheepProperties 只需要實現序列化接口即可,如果屬性中還有引用對象,當然也都需要實現序列化接口,就不需要實現copyOnSelf方法
class SheepProperties implements Serializable {
    private String placeOfProduction;
    private String breeding;

    public SheepProperties(String placeOfProduction, String breeding) {
        this.placeOfProduction = placeOfProduction;
        this.breeding = breeding;
    }
	//...省略一些常規方法
}
  • 最後還是通過測試方法,看一下克隆出來的對象的引用屬性是否爲同一個
/**
 * @program: ade-someproblem
 * @author: cade franklin
 * @create: 2019-12-22 22:12
 **/
public class DeepPrototype {

    public static void main(String[] args) throws Exception {
        Sheep sheep1 = new Sheep("sheep1", 2);
        SheepProperties properties = new SheepProperties("中國", "山羊");
        sheep1.setProperties(properties);
        Sheep sheep2 = (Sheep) sheep1.copyOnSelf();

        System.out.println(sheep1);
        System.out.println(sheep2);

        System.out.println(sheep1.hashCode());
        System.out.println(sheep2.hashCode());

        System.out.println(sheep1.getProperties().hashCode());
        System.out.println(sheep2.getProperties().hashCode());
    }
}

在這裏插入圖片描述

  • 屬性的hashcode不是同一個,證實引用類型也被克隆了,但是我們在SheepProperties中並沒有去寫序列化相關代碼,擴展性提高了,這是推薦使用的深拷貝,也是所謂的原型模式

總結

  • 原型模式簡單來說就是對象的複製,看了一些其他同學關於原型模式的寫法,和我的不完全一樣,還是那句話形不似,但神似,精神一脈相承,足矣!當然如果你有任何問題,歡迎私信我,我們一起討論,嘻嘻😳

願你不捨愛與自由。

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