一天一個設計模式(四) - 原型模式(Prototype)

前言

原型模式屬於對象的建立模式。經過給出一個原型對象來指明全部建立的對象的類型,而後用這個原型對象提供的複製辦法建立出更多同類型的對象。java

原型模式的結構

原型模式要求對象實現一個能夠克隆自身的接口(類型)。這樣一來,經過原型實例建立新的對象,就不須要關心這個實例自己的類型,只須要實現克隆自身的方法,也而無需再去經過new來建立。編程

原型類型的表現形式

  1. 簡單形式
  2. 登記形式

正文

簡單形式

相關角色

  1. 客戶(Client)角色客戶類提出建立對象的請求;
  2. 抽象原型(Prototype)角色:這是一個抽象角色,一般由一個Java接口或者Java抽象類實現。此角色定義了的具體原型類所需的實現的方法。
  3. 具體原型(Concrete Prototype)角色:此角色須要實現抽象原型角色要求的克隆相關接口

示例代碼

Prototype.java後端

/** * 抽象原型角色 */
public abstract class Prototype {
    private String id;

    public Prototype(String id) {
        this.id = id;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    /** * 克隆自身的方法 * @return 一個從自身克隆出來的對象。 */
    public abstract Prototype clone();
}
複製代碼

ConcretePrototype1.java緩存

public class ConcretePrototype1 extends Prototype {
    public ConcretePrototype1(String id) {
        super(id);
    }

    public Prototype clone() {
        Prototype prototype = new ConcretePrototype1(this.getId());
        return prototype;
    }
}
複製代碼

ConcretePrototype2.java多線程

public class ConcretePrototype2 extends Prototype {
    public ConcretePrototype2(String id) {
        super(id);
    }

    public Prototype clone() {
        Prototype prototype = new ConcretePrototype2(this.getId());
        return prototype;
    }
}
複製代碼

運行結果

登記形式

相關角色

  1. 客戶(Client)角色客戶類提出建立對象的請求;
  2. 抽象原型(Prototype)角色:這是一個抽象角色,一般由一個Java接口或者Java抽象類實現。此角色定義了的具體原型類所需的實現的方法。
  3. 具體原型(Concrete Prototype)角色:此角色須要實現抽象原型角色要求的克隆相關接口
  4. 原型管理器(Prototype Manager)角色:提供各類原型對象建立管理

示例代碼

除了原型管理器Prototype Manager之外,登記模式簡單模式並沒有其餘差別。架構

Prototype.java W框架

public interface Prototype {
    public Prototype clone();
    public String getName();
    public void setName(String name);
}
複製代碼

ConcretePrototype1.java異步

public class ConcretePrototype1 implements Prototype {
    private String name;

    @Override
    public String getName() {
        return this.name;
    }

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

    @Override
    public Prototype clone() {
        Prototype prototype = new ConcretePrototype1();
        prototype.setName(this.name);
        return prototype;
    }

    @Override
    public String toString() {
        return "ConcretePrototype1 [name=" + name + "]";
    }

}
複製代碼

ConcretePrototype2.java分佈式

public class ConcretePrototype2 implements Prototype {
    private String name;

    @Override
    public String getName() {
        return this.name;
    }

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

    @Override
    public Prototype clone() {
        Prototype prototype = new ConcretePrototype2();
        prototype.setName(this.name);
        return prototype;
    }

    @Override
    public String toString() {
        return "ConcretePrototype2 [name=" + name + "]";
    }
}
複製代碼

PrototypeManager.javaide

public class PrototypeManager {
    /** * 用來記錄原型的編號同原型實例的對象關係 */
    private static Map<String, Prototype> map = new HashMap<>();

    /** * 私有化構造方法,避免從外部建立實例 */
    private PrototypeManager() {
    }

    /** * 向原型管理器裏面添加或者修改原型實例 * * @param prototypeId 原型編號 * @param prototype 原型實例 */
    public static void setProtoType(String prototypeId, Prototype prototype) {
        map.put(prototypeId, prototype);
    }

    /** * 根據原型編號從原型管理器裏面移除原型實例 * * @param prototypeId 原型編號 */
    public static void removePrototype(String prototypeId) {
        map.remove(prototypeId);
    }

    /** * 根據原型編號獲取原型實例 * * @param prototypeId 原型編號 * @return 原型實例對象 * @throws Exception 若是根據原型編號沒法獲取對應實例,則提示異常「您但願獲取的原型尚未註冊或已被銷燬」 */
    public static Prototype getPrototype(String prototypeId) throws Exception {
        Prototype prototype = map.get(prototypeId);

        if (prototype == null) {
            throw new Exception("您但願獲取的原型尚未註冊或已被銷燬");
        }

        return prototype;
    }

}
複製代碼

Client.java

public class Client {
    public static void main(String[] args) {
        try {
            // 建立第一個實例
            Prototype p1 = new ConcretePrototype1();
            // 註冊第一個實例
            PrototypeManager.setProtoType("p1", p1);

            // 克隆第一個實例的原型
            Prototype p3 = PrototypeManager.getPrototype("p1").clone();
            p3.setName("張三");
            System.out.println("第一個實例的副本:" + p3);

            // 建立第二個實例
            Prototype p2 = new ConcretePrototype2();
            // 註冊第二個實例
            PrototypeManager.setProtoType("p2", p2);

            // 克隆第二個實例的原型
            Prototype p4 = PrototypeManager.getPrototype("p2").clone();
            p4.setName("李四");
            System.out.println("第二個實例的副本:" + p4);

            // 註銷第一個實例
            PrototypeManager.removePrototype("p1");
            // 再次克隆第一個實例的原型
            Prototype p5 = PrototypeManager.getPrototype("p1").clone();
            p5.setName("王五");
            System.out.println("第一個實例的副本:" + p5);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
複製代碼

運行結果

二者之間的比較

簡單形式和登記形式的原型模式各有其長處和短處。

  1. 若是要建立的原型對象數據較少並且比較固定的話,能夠採用第一種形式。在這種狀況下,原型對象的引用能夠由客戶端本身保存。
  2. 若是要建立的原型對象數據不固定的話,能夠採用第二種形式。在這種狀況下,客戶端不保存對原型對象的引用,這個任務被交給原型管理器角色。在克隆一個對象以前,客戶端能夠查看管理員對象是否已經有一個知足要求的原型對象。若是有,能夠從原型管理器角色中取得這個對象引用;若是沒有,客戶端就須要自行復制此原型對象。

總結

原型模式的優勢

原型模式容許在運行時動態改變具體的實現類型。原型模式能夠在運行期間,有客戶來註冊符合原型接口的實現類型,也能夠動態的改變具體的實現類型,看起來接口沒有任何變化,可是其實運行的已是另一個類實體了。由於克隆一個原型對象就相似於實例化一個類

原型模式的缺點

原型模式最主要的缺點是每個類都必需要配備一個克隆方法。配備克隆方法須要對類的功能進行通盤考慮,這對於全新的類來講並非很難,可是對於已有的類來講並不容易。


歡迎關注技術公衆號: 零壹技術棧

零壹技術棧

本賬號將持續分享後端技術乾貨,包括虛擬機基礎,多線程編程,高性能框架,異步、緩存和消息中間件,分佈式和微服務,架構學習和進階等學習資料和文章。

相關文章
相關標籤/搜索