【23種設計模式專題】三 原型模式

程序猿學社的GitHub,歡迎Star
github技術專題
本文已記錄到github

前言

通過前面兩章,我們已經知道單例模式,工廠模式,本文就來看一看原型模式

概念

用原型實例指定創建對象的種類,並且通過拷貝這些原型創建新的對象。原型模式屬於一種創建型的設計模式

  • 可以理解爲克隆技術,克隆羊
  • copy技術,直接原封不動的copy一個內容
  • 修真小說,到了元嬰期,就是肉體被毀滅,只要元嬰沒有被滅,還可以復活。
  • 王者榮耀裏面最新出來的一個英雄,鏡,可以製造一個分身。分身也擁有攻擊力

實戰

場景

隔壁老王: “社長,你們公司需要填寫週報嗎?”
社長: “需要填寫的,而且每週,都會有一次周例會”
隔壁老王: “填寫週報好浪費時間,還得排版,再填寫內容”
社長: “你是怎麼實現的”
隔壁老王: “我每次重新生成一個excel,再排版的”
社長: “老鐵,你這太實誠咯”

傳統方式

package com.cxyxs.designmode.prototype;

import lombok.Data;

import java.util.Date;

/**
 * Description:
 * Author: 程序猿學社
 * Date:  2020/4/12 18:42
 * Modified By:
 */
@Data
public class Weekly {
    private String name;
    /** 本週計劃 */
    private String weekPlan;
    /** 下週計劃 */
    private String nextWeekPlan;
    /**  填寫時間 */
    private Date createTime;

    public Weekly(String name, String weekPlan, String nextWeekPlan, Date createTime) {
        this.name = name;
        this.weekPlan = weekPlan;
        this.nextWeekPlan = nextWeekPlan;
        this.createTime = createTime;
    }
}
  • 使用@Data註解,實際上就是簡化代碼,無需寫set get方法,使用了lombok插件
package com.cxyxs.designmode.prototype;

import java.util.Date;

/**
 * Description:
 * Author: 程序猿學社
 * Date:  2020/4/12 18:45
 * Modified By:
 */
public class WeeklyTest {
    public static void main(String[] args) {
        Weekly weekly = new Weekly("隔壁小王","用戶模塊開發","訂單模塊開發",new Date());
        System.out.println("--------本週週報--------");
        System.out.println(weekly.toString());
        System.out.println("--------下週週報--------");
        Weekly weekly1 = new Weekly("隔壁小王","訂單模塊開發","訂單模塊開發相關單元測試",new Date());
        System.out.println(weekly1.toString());
    }
}


隔壁老王: “社長,有沒有什麼方法,可以簡化週報這塊,提高我編寫週報的效率”
社長: “直接copy一份,上一次編寫的週報(原型模式),在此之上,再做修改”

原型模式

package com.cxyxs.designmode.prototype.shallow;

import lombok.Data;

import java.util.Date;

/**
 * Description:
 * Author: 程序猿學社
 * Date:  2020/4/12 18:42
 * Modified By:
 */
@Data
public class Weekly implements  Cloneable{
    private String name;
    /** 本週計劃 */
    private String weekPlan;
    /** 下週計劃 */
    private String nextWeekPlan;
    /**  填寫時間 */
    private Date createTime;

    public Weekly(String name, String weekPlan, String nextWeekPlan, Date createTime) {
        this.name = name;
        this.weekPlan = weekPlan;
        this.nextWeekPlan = nextWeekPlan;
        this.createTime = createTime;
    }

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

通過上面的代碼,我們小小的梳理一下,實現的過程
實現原型模式就兩步:

  • 實現Cloneable接口
  • 重寫clone方法
  • 學過java的社友,都知道,Object是一切類的老祖宗,代碼裏面的super.clone(),實際上,就是調用Object的clone方法,通過查看他的源碼,發現,又遇到native方法,說明實現克隆不是通過java實現的(c或者c++)
package com.cxyxs.designmode.prototype.shallow;


import java.util.Date;

/**
 * Description:
 * Author: 程序猿學社
 * Date:  2020/4/12 18:45
 * Modified By:
 */
public class WeeklyTest {
    public static void main(String[] args) throws  Exception{
        Date date = new Date();
        Weekly weekly = new Weekly("隔壁小王","用戶模塊開發","訂單模塊開發",date);
        System.out.println("--------本週週報--------");
        System.out.println(weekly.toString());
        System.out.println("--------下週週報--------");
        Weekly weekly1 = (Weekly) weekly.clone();    //使用克隆(copy方式)
        //Weekly weekly1 = new Weekly("隔壁小王","訂單模塊開發","訂單模塊開發相關單元測試",new Date());
        System.out.println(weekly1.toString());
        System.out.println(weekly == weekly1);
    }
}

  • 可以發現結果爲false,說明重新生成了一個對象,而且克隆的對象和原來的一模一樣,回顧一下,我們第一章學習過的單例模式,又增加了一種,可以破解單例的方法。
    單例模式,誰說程序猿沒有女(男)朋友?

社長: “實際上,上面這種寫法還存在一點的問題”
隔壁老王: “社長,存在什麼問題”
社長: “上面這種寫法,只能實現淺克隆,沒有實現完全的克隆,我們先通過一個例子來看一看把”

淺克隆

package com.cxyxs.designmode.prototype.shallow;


import java.util.Date;

/**
 * Description:
 * Author: 程序猿學社
 * Date:  2020/4/12 18:45
 * Modified By:
 */
public class WeeklyTest1 {
    public static void main(String[] args) throws  Exception{
        Date date = new Date();

        Weekly weekly = new Weekly("隔壁小王","用戶模塊開發","訂單模塊開發",date);
        Weekly weekly1 = (Weekly) weekly.clone();    //使用克隆(copy方式)
        System.out.println(weekly.toString());
        System.out.println(weekly1.toString());
        //修改克隆後對象的值
        date.setTime(1546315871);
        System.out.println("--------------");
        System.out.println(weekly.toString());
        System.out.println(weekly1.toString());
    }
}

  • 先實現克隆後,我們再修改createTime的值,可以發現,修改後,克隆前和克隆後的值都一樣,說明這兩個對象的指向的都是同一個地方。

淺克隆

  • 淺克隆會進行值傳遞(只是針對基本數據類型)
  • 如果是引用數據類型,淺克隆會進行引用傳遞,實際上,就是把引用地址copy一份給新的對象,實際上,都是指向同一個地址。

深克隆

重寫clone方法實現深克隆

package com.cxyxs.designmode.prototype.shallow;

import lombok.Data;

import java.util.Date;

/**
 * Description:
 * Author: 程序猿學社
 * Date:  2020/4/12 18:42
 * Modified By:
 */
@Data
public class Weekly implements  Cloneable{
    private String name;
    /** 本週計劃 */
    private String weekPlan;
    /** 下週計劃 */
    private String nextWeekPlan;
    /**  填寫時間 */
    private Date createTime;

    public Weekly(String name, String weekPlan, String nextWeekPlan, Date createTime) {
        this.name = name;
        this.weekPlan = weekPlan;
        this.nextWeekPlan = nextWeekPlan;
        this.createTime = createTime;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        try {
            Weekly weekly = (Weekly) super.clone();
            //把數據克隆
            Date clone = (Date) createTime.clone();
            weekly.setCreateTime(clone);
            return weekly;
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }
}

  • 因爲考慮到createTime,指向的是同一個引用,所以,我們每次克隆的時候,都把這個字段重新克隆一次。
  • 通過上圖,可以看出,修改時間,只是克隆前的值發生了改變。 可以這樣理解,在修真小說內,到了分神期,可以修煉第二分身,實際上,你攻擊本體時,只是本體受到傷害,就算本體死亡,第二分身還可以繼續存活。

隔壁老王: “社長,看了一下,你寫的代碼,這還是屬性的,如果有上百個屬性,我這一個個改,也忒麻煩咯。有沒有不用這樣一個個設置的方法”
社長: “這種方法的copy,實際上是內存的copy,直接操作內存的,也是性能最好的,如果你覺得這種方式太麻煩了,我們可以通過序列化和反序列的方式來實現”

通過序列化實現深克隆

package com.cxyxs.designmode.prototype.shallow.seri;

import lombok.Data;

import java.io.*;
import java.util.Date;

/**
 * Description:
 * Author: 程序猿學社
 * Date:  2020/4/12 18:42
 * Modified By:
 */
@Data
public class Weekly implements Serializable {
    private String name;
    /** 本週計劃 */
    private String weekPlan;
    /** 下週計劃 */
    private String nextWeekPlan;
    /**  填寫時間 */
    private Date createTime;

    public Weekly(String name, String weekPlan, String nextWeekPlan, Date createTime) {
        this.name = name;
        this.weekPlan = weekPlan;
        this.nextWeekPlan = nextWeekPlan;
        this.createTime = createTime;
    }

    public Object copy() throws  Exception{
        //序列化
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this);

        //反序列化
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        Weekly weekly = (Weekly) ois.readObject();
        return weekly;
    }
}

實現序列化兩部曲:

  • 實現Serializable接口
  • 實現序列化和反序列化對應的代碼
package com.cxyxs.designmode.prototype.shallow.seri;



import java.util.Date;

/**
 * Description:
 * Author: 程序猿學社
 * Date:  2020/4/12 18:45
 * Modified By:
 */
public class WeeklyTest2 {
    public static void main(String[] args) throws  Exception{
        Date date = new Date();

        Weekly weekly = new Weekly("隔壁老王","用戶模塊開發","訂單模塊開發",date);
        Weekly weekly1 = (Weekly) weekly.copy();    //使用克隆(copy方式)
        System.out.println(weekly.toString());
        System.out.println(weekly1.toString());
        //修改克隆後對象的值
        date.setTime(11);
        System.out.println("--------------");
        System.out.println(weekly.toString());
        System.out.println(weekly1.toString());
    }
}
  • 通過這種方式實現對象的深度克隆,無需一個個設置引用類型屬性的clone方法。

總結: 如果,需要創建大量的對象,建議使用原型模式。實際上他是不符合ocp原則的。應該實現對擴展開放,對修改關閉。不會調用類的重構方法 ,可以破解單例模式。


原創不易,不要白嫖,覺得有用的社友,給我點贊,讓更多的老鐵看到這篇文章。
因技術能力有限,如文中有不合理的地方,希望各位大佬指出,在下方評論留言,謝謝,希望大家一起進步,一起成長。

作者:程序猿學社
原創公衆號:『程序猿學社』,專注於java技術棧,分享java各個技術系列專題,以及各個技術點的面試題。
原創不易,轉載請註明來源(註明:來源於公衆號:程序猿學社, 作者:程序猿學社)。

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