原型模式,一個無用的模式?

目錄

一、簡介

1、定義

2、使用場景

3、模式分析

4、優缺點

二、實現

1、UML類圖

2、Prototype

3、ConcretePrototypeA

4、ConcretePrototypeB

5、PrototypeCache

6、Client_Prototype

三、原型模式分析

1、問題描述

 2、Person_Prototype

3、Cilent客戶端

4、分析


一、簡介

1、定義

原型模式就是從一個對象再創建另一個可定製的對象,而不需要知道任何創建的細節。

2、使用場景

 我們現在一般會使用new關鍵字指定類名生成類的實例(PS:我們以前使用java.lang.Cloneable的一個很大原因是使用new創建對象的速度相對來說會慢一些,隨着JVM性能的提升,new的速度和Object的clone()方法的速度差不多了。)。

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

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

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

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

如果想要讓生成實例的框架不再依賴於具體的類,這時,不能指定類名來生成實例,而要事先“註冊”一個“原型”實例,然後通過複製該實例來生成新的實例。

3、模式分析

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

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

注意: `java.lang.Cloneable 只是起到告訴程序可以調用clone方法的作用,它本身並沒有定義任何方法。

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

4、優缺點

原型模式的優點:

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

原型模式的缺點:

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

二、實現

1、UML類圖

2、Prototype

package com.mfc.design.原型模式;

/**
 * @author MouFangCai
 * @date 2019/10/12 14:41
 */
public abstract class Prototype implements Cloneable  {

    private String id;
    protected String type;

    abstract void draw();

    public String getType(){
        return type;
    }

    public String getId() {
        return id;
    }

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

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

3、ConcretePrototypeA

package com.mfc.design.原型模式;

/**
 * @author MouFangCai
 * @date 2019/10/12 14:45
 */
public class ConcretePrototypeA extends Prototype {
    @Override
    void draw() {
        System.out.println("這是ConcretePrototypeA的draw()");
    }
    public ConcretePrototypeA(){
        type = "ConcretePrototypeA";
    }
}

4、ConcretePrototypeB

package com.mfc.design.原型模式;

/**
 * @author MouFangCai
 * @date 2019/10/12 14:45
 */
public class ConcretePrototypeB extends Prototype {
    @Override
    void draw() {
        System.out.println("這是ConcretePrototypeB的draw()");
    }
    public ConcretePrototypeB(){
        type = "ConcretePrototypeB";
    }

}

5、PrototypeCache

package com.mfc.design.原型模式;

import java.util.Hashtable;

/**
 * @author MouFangCai
 * @date 2019/10/12 14:45
 */
public class PrototypeCache {

    // 維護一個註冊表
    private static Hashtable<String, Prototype> prototypeMap = new Hashtable<>();

    // 提供一個獲取新實例的方法
    public static Prototype getprototype(String prototypeId) {
        Prototype cached = prototypeMap.get(prototypeId);
        return (Prototype) cached.clone();
    }

    // 對每種形狀都運行數據庫查詢,並創建該形狀
    // prototypeMap.put(prototypeId, Prototype);
    // 例如,我們要添加2種Prototype
    public static void loadCache() {
        ConcretePrototypeA A = new ConcretePrototypeA();
        A.setId("1");
        prototypeMap.put(A.getId(),A);

        ConcretePrototypeB B = new ConcretePrototypeB();
        B.setId("2");
        prototypeMap.put(B.getId(),B);
    }
}

6、Client_Prototype

package com.mfc.design.原型模式;

/**
 * @author MouFangCai
 * @date 2019/10/12 15:00
 */
public class Client_Prototype {

    public static void main(String[] args) {
        PrototypeCache.loadCache();

        Prototype clonedPrototype1 = (Prototype) PrototypeCache.getprototype("1");
        System.out.println("Prototype : " + clonedPrototype1.getType());

        Prototype clonedPrototype2 = (Prototype) PrototypeCache.getprototype("2");
        System.out.println("Prototype : " + clonedPrototype2.getType());
    }
}

三、原型模式分析

上面這個例子,參考自菜鳥教程:https://www.runoob.com/design-pattern/prototype-pattern.html(小編不是很理解這個原型模式的例子,感覺似乎沒什麼實際意義,只是new對象的操作換了個地方而已)

從上面這個例子分析,總感覺原型模式似乎有些問題。所以小編再次參考閱讀了《大話設計模式》,詳情請見下面的例子

1、問題描述

比如說,有一個使用場景需要不同的Person對象但是其name屬性一樣,但是age和score屬性需要根據環境不同而設置不同的值

 2、Person_Prototype

package com.mfc.design.原型模式2;

/**
 * @author MouFangCai
 * @date 2019/10/12 16:12
 */
// 原型Prototype
public class Person_Prototype implements Cloneable {

    private String name;
    private int age;
    private int score;

    // 設置需要複製的屬性(公有屬性)
    public Person_Prototype(String name){
        this.name = name;
    }

    // 設置需要自定義的屬性
    public void setMessage(int age, int score){
        this.age =age;
        this.score =score;
    }

    public void dispaly(){
        System.out.println("name:" + name + "----age:"+ age + "----score:" + score);
    }

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

3、Cilent客戶端

package com.mfc.design.原型模式2;

/**
 * @author MouFangCai
 * @date 2019/10/12 15:00
 */
public class Client_Prototype {

    public static void main(String[] args) throws CloneNotSupportedException {

        Person_Prototype p1 = new Person_Prototype("mfc");
        p1.setMessage(18, 22);

        // 複製一個新的Person,並修改對應的細節屬性
        Person_Prototype p2 = (Person_Prototype)p1.clone();
        p2.setMessage(24,44 );

        System.out.println("是否是同一個對象:" + (p1 == p2));
        p1.dispaly();
        p2.dispaly();
    }
}

結果如下:

是否是同一個對象:false
name:mfc----age:18----score:22
name:mfc----age:24----score:44

Process finished with exit code 0

4、分析

從運行結果,我們可以看到,Person對象被複制了一個,同時name屬性保持一致,age被重新自定義,達到我們預期要求

備註:這只是 淺複製,如果Person對象有一個私有屬性是對象時,就需要使用 深複製 進行實現,有興趣者可自行百度。

 

發佈了101 篇原創文章 · 獲贊 177 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章