深刻理解原型模式 ——經過複製生成實例

Java面試通關手冊(Java學習指南,歡迎Star,會一直完善下去,歡迎建議和指導):https://github.com/Snailclimb/Java_Guidehtml

系列文章回顧:

設計模式專欄
深刻理解單例模式
深刻理解工廠模式java

深刻理解建造者模式 ——組裝複雜的實例git

歷史文章推薦:

一隻準程序猿的嘮叨github

多是最漂亮的Spring事務管理詳解面試

Java多線程學習(八)線程池與Executor 框架c#

面試中關於Redis的問題看這篇就夠了設計模式

目錄:

[TOC]安全

一 原型模式介紹

在面向對象系統中,使用原型模式來複制一個對象自身,從而克隆出多個與原型對象如出一轍的對象。服務器

另外在軟件系統中,有些對象的建立過程較爲複雜,並且有時候須要頻繁建立,原型模式經過給出一個原型對象來指明所要建立的對象的類型,而後用複製這個原型對象的辦法建立出更多同類型的對象,這就是原型模式的意圖所在。微信

孫悟空分身

1.1 定義

GOF給出的原型模式定義以下:

Specify the kind of objects to create using a prototypical instance, and create new objects by copying this prototype. (使用原型實例指定將要建立的對象類型,經過複製這個實例建立新的對象。)

1.2 原型模式適用場景

咱們如今通常會使用new關鍵字指定類名生成類的實例(PS:咱們之前使用java.lang.Cloneable的一個很大緣由是使用new建立對象的速度相對來講會慢一些,隨着JVM性能的提高,new的速度和Object的clone()方法的速度差很少了。)。

使用new關鍵字建立類的時候必須指定類名,可是在開發過程當中也會有「在不指定類名的前提下生成實例」的需求。例如,在下面這些狀況下,就須要根據現有的實例來生成新的實例。

1) 對象種類繁多,沒法將他們整合到一個類的時候;

2) 難以根據類生成實例時;

3) 想解耦框架與生成的實例時。

若是想要讓生成實例的框架再也不依賴於具體的類,這時,不能指定類名來生成實例,而要事先「註冊」一個「原型」實例,而後經過複製該實例來生成新的實例。

1.3 模式分析

在原型模式結構中定義了一個抽象原型類,全部的Java類都繼承自 java.lang.Object,而Object類提供一個clone()方法,能夠將一個Java對象複製一份。所以在Java中能夠直接使用Object提供的clone()方法來實現對象的克隆,Java語言中的原型模式實現很簡單。

可以實現克隆的Java類必須實現一個標識接口Cloneable,表示這個Java類支持複製。若是一個類沒有實現這個接口可是調用了clone()方法,Java編譯器將拋出一個CloneNotSupportedException異常。

注意: `java.lang.Cloneable 只是起到告訴程序能夠調用clone方法的做用,它自己並無定義任何方法。

在使用原型模式克隆對象時,根據其成員對象是否也克隆,原型模式能夠分爲兩種形式:深克隆淺克隆

關於深克隆淺克隆 的詳細內容能夠參考:詳解Java中的clone方法

1.4 模式優缺點分析

原型模式的優勢:

  • 當建立新的對象實例較爲複雜時,使用原型模式能夠簡化對象的建立過程,經過一個已有實例能夠提升新實例的建立效率。
  • 能夠動態增長或減小產品類。
  • 原型模式提供了簡化的建立結構。
  • 可使用深克隆的方式保存對象的狀態。

原型模式的缺點:

  • 須要爲每個類配備一個克隆方法,並且這個克隆方法須要對類的功能進行通盤考慮,這對全新的類來講不是很難,但對已有的類進行改造時,不必定是件容易的事,必須修改其源代碼,違背了「開閉原則」。
  • 在實現深克隆時須要編寫較爲複雜的代碼。

二 示例程序

下面示例程序的做用是將字符串放入方框中顯示出來或者是加了下劃線顯示出來。

類和接口一覽表:

類和接口一覽表

示例程序類圖:

2.1 Product接口 (Prototype)

Product接口是複製功能接口,該接口繼承了java.lang.Cloneable(只有實現了該接口的類的實例才能夠調用clone()方法複製實例,不然會拋出異常).
另外須要注意:`java.lang.Cloneable 只是起到告訴程序能夠調用clone方法的做用,它自己並無定義任何方法。

package prototype_pattern;

public interface Product extends Cloneable{
   //use方法是用於「使用」的方法,具體怎麼「使用」,則被交給子類去實現。
    public abstract void use(String s);
    //creatClone方法是用於複製實例的方法
    public abstract Product creatClone();

}

2.2 Manager類(Client)

Manager類使用Product接口來複制實例。

Product接口以及Manager類的代碼徹底沒有出如今MessageBox類UnderlinePen類的名字,所以這意味着咱們能夠獨立地修改Product接口以及Manager類,不受MessageBox類UnderlinePen類的影響。這是很是重要的,由於 一旦在類中使用到了別的類名,就意味着該類與其餘類緊密的地耦合在了一塊兒 。在Manager類中,並無寫明具體的類名, 僅僅使用了Product這個接口名。也就是說,Product接口成爲了鏈接Manager類與其餘具體類之間的橋樑。

package prototype_pattern;

import java.util.HashMap;

public class Manager {
    //保存實例的「名字」和「實例」之間的對應關係
    private HashMap<String, Product> showcase=new HashMap<String, Product>();
    //register方法將接收到的一組「名字」和「Product接口」註冊到showcase中。這裏Product是實現Product接口的實例,具體還未肯定
    public void register(String name ,Product product){
        showcase.put(name, product);
    }
    public Product create(String productname){
        Product p=showcase.get(productname);
        return p.creatClone();
    }

}

2.3 MessageBox類(ConcreteProtorype)

裝飾方框樣式的具體原型,實現了 Product接口,實現複製現有實例並生成新實例的方法。

package prototype_pattern;

public class MessageBox implements Product {
    //保存的是裝飾方框使用的字符樣式
    private char decochar;

    public MessageBox(char decochar) {
        this.decochar = decochar;
    }

    @Override
    public void use(String s) {
     int length=s.getBytes().length;
     for (int i = 0; i < length+4; i++) {
            System.out.print(decochar);    
    }
     System.out.println("");
     System.out.println(decochar+" "+s+" "+decochar);
     for (int i = 0; i < length+4; i++) {
        System.out.print(decochar);
    }
     System.out.println("");
    }
    
    //該方法用於複製本身
    @Override
    public Product creatClone() {
        Product p=null;
        try {
            p=(Product) clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return p;
    }

}

只有類本身(或是它的子類)可以調用Java語言中定義的clone方法。當其餘類要求複製實例時,必須先調用createClone這樣的方法,而後在該方法內部在調用clone方法。

2.4 UnderlinePen類(ConcreteProtorype)

下劃線樣式的具體原型,實現了Product接口,用於實現複製現有實例並生成新實例的方法。UnderlinePen類的實現幾乎和 MessageBox類同樣,不一樣的可能只是use方法的實現。

package prototype_pattern;

public class UnderlinePen implements Product {

    private char ulchar;

    public UnderlinePen(char ulchar) {
        this.ulchar = ulchar;
    }

    @Override
    public void use(String s) {
        int length = s.getBytes().length;
        System.out.println("\""+s+"\"");
        for (int i = 0; i <length+2; i++) {
            System.out.print(ulchar);
            
        }
        System.out.println("");
    }

    @Override
    public Product creatClone() {
        Product p=null;
        try {
            p=(Product) clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return p;
    }

}

2.5 Main類

Main類首先生成Manager實例。接着,在Manager實例中經過`register方法註冊了UnderlinePen類的實例(帶名字)和MessageBox類的實例(帶名字)。

package prototype_pattern;

public class Main {

    public static void main(String[] args) {
        Manager manager = new Manager();
        UnderlinePen underlinePen=new UnderlinePen('~');
        MessageBox mbox=new MessageBox('*');
        MessageBox sbox=new MessageBox('/');
        manager.register("Strong message", underlinePen);
        manager.register("Waring Box", mbox);
        manager.register("Slash Box", sbox);
        Product p1=manager.create("Strong message");
        p1.use("hello world");
        Product p2=manager.create("Waring Box");
        p2.use("hello world");
        Product p3=manager.create("Slash Box");
        p3.use("hello world");
    }

}

運行結果:

運行結果

三 原型模式的角色分析

經過上面的例子,相信你們對於原型模式有了更進一步的認識,下面咱們看看原型模式的幾個登場角色。

3.1 Prototype(抽象原型類)

Product角色負責定義用於複製現有實例來生成新實例的方法。在示例程序中的Product接口就是該角色。

3.2 ConcretePrototype(具體原型類)

ConcretePrototype角色負責實現複製現有實例並生成新實例的方法。在示例程序中,MessageBox和UnderlinePen都是該角色。

3.3 Client(客戶類/使用者)

Client角色負責使用複製實例的方法生成新的實例。在示例程序中,Manager類扮演的就是該角色。

Prototype模式的類圖:
Prototype模式的類圖

四 原型模式的實際應用案例

(1) 原型模式應用於不少軟件中,若是每次建立一個對象要花大量時間,原型模式是最好的解決方案。不少軟件提供的複製(Ctrl + C)粘貼(Ctrl + V)操做就是原型模式的應用,複製獲得的對象與原型對象是兩個類型相同但內存地址不一樣的對象,經過原型模式能夠大大提升對象的建立效率。

(2) 在Struts2中爲了保證線程的安全性,Action對象的建立使用了原型模式,訪問一個已經存在的`Action對象時將經過克隆的方式建立出一個新的對象,從而保證其中定義的變量無須進行加鎖實現同步,每個Action中都有本身的成員變量,避免Struts1因使用單例模式而致使的併發和同步問題。

(3) 在Spring中,用戶也能夠採用原型模式來建立新的bean實例,從而實現每次獲取的是經過克隆生成的新實例,對其進行修改時對原有實例對象不形成任何影響。

五 總結

本文主要介紹了:什麼是原型模式、原型模式的優缺點以及使用場景。另外,簡單介紹了深拷貝和淺拷貝以及原型模式的實際應用案例。

參考:

《圖解設計模式》

歡迎關注個人微信公衆號:" Java面試通關手冊"(一個有溫度的微信公衆號,無廣告,單純技術分享,期待與你共同進步~~~堅持原創,分享美文,分享各類Java學習資源。)

最後,就是使用阿里雲服務器一段時間後,感受阿里雲真的很不錯,就申請作了阿里雲大使,而後這是個人優惠券地址.

相關文章
相關標籤/搜索