23種設計模式之原型模式2

用來解決上述問題的一個合理的解決方案就是原型模式。那麼什麼是原型模式呢?

(1)原型模式定義

 

(2)應用原型模式來解決的思路

仔細分析上面的問題,在saveOrder方法裏面,已經有了訂單接口類型的對象實例,是從外部傳入的,但是這裏只是知道這個實例對象的種類是訂單的接口類型,並不知道其具體的實現類型,也就是不知道它到底是個人訂單還是企業訂單,但是現在需要在這個方法裏面創建一個這樣的訂單對象,看起來就像是要通過接口來創建對象一樣。

原型模式就可以解決這樣的問題,原型模式會要求對象實現一個可以“克隆”自身的接口,這樣就可以通過拷貝或者是克隆一個實例對象本身,來創建一個新的實例。如果把這個方法定義在接口上,看起來就像是通過接口來創建了新的接口對象。

這樣一來,通過原型實例創建新的對象,就不再需要關心這個實例本身的類型,也不關心它的具體實現,只要它實現了克隆自身的方法,就可以通過這個方法來獲取新的對象,而無須再去通過new來創建。

9.2.1  模式結構和說明

原型模式的結構如圖9.1所示:

 

圖9.1  原型模式結構示意圖

Prototype:

聲明一個克隆自身的接口,用來約束想要克隆自己的類,要求它們都要實現這裏定義的克隆方法。

ConcretePrototype:

實現Prototype接口的類,這些類真正實現了克隆自身的功能。

Client:

使用原型的客戶端,首先要獲取到原型實例對象,然後通過原型實例克隆自身來創建新的對象實例。

9.2.3  原型模式示例代碼

(1)先來看看原型接口的定義,示例代碼如下:

/**

 * 聲明一個克隆自身的接口

 */

public interface Prototype {

    /**

     * 克隆自身的方法

     * @return 一個從自身克隆出來的對象

     */

    public Prototype clone();

}

(2)接下來看看具體的原型實現對象,示例代碼如下:

/**

 * 克隆的具體實現對象

 */

public class ConcretePrototype1 implements Prototype {

    public Prototype clone() {

       //最簡單的克隆,新建一個自身對象,由於沒有屬性,就不去複製值了

       Prototype prototype = new ConcretePrototype1();

       return prototype;

    }

}

/**

 * 克隆的具體實現對象

 */

public class ConcretePrototype2 implements Prototype {

    public Prototype clone() {

       //最簡單的克隆,新建一個自身對象,由於沒有屬性,就不去複製值了

       Prototype prototype = new ConcretePrototype2();

       return prototype;

    }

}

       爲了跟上面原型模式的結構示意圖保持一致,因此這兩個具體的原型實現對象,都沒有定義屬性。事實上,在實際使用原型模式的應用中,原型對象多是有屬性的,克隆原型的時候也是需要克隆原型對象的屬性的,特此說明一下。

(3)再看看使用原型的客戶端,示例代碼如下:

/**

 * 使用原型的客戶端

 */

public class Client {

    /**

     * 持有需要使用的原型接口對象

     */

    private Prototype prototype;

    /**

     * 構造方法,傳入需要使用的原型接口對象

     * @param prototype 需要使用的原型接口對象

     */

    public Client(Prototype prototype){

       this.prototype = prototype;

    }

    /**

     * 示意方法,執行某個功能操作

     */

    public void operation(){

       //會需要創建原型接口的對象

       Prototype newPrototype = prototype.clone();

    }

}

9.2.4  使用原型模式重寫示例

       要使用原型模式來重寫示例,先要在訂單的接口上定義出克隆的接口,然後要求各個具體的訂單對象克隆自身,這樣就可以解決:在訂單處理對象裏面通過訂單接口來創建新的訂單對象的問題。

       使用原型模式來重寫示例的結構如圖9.2所示:

 

圖9.2  使用原型模式來重寫示例的結構示意圖

下面一起來看看具體的實現。

(1)複製誰和誰來複制的問題

有了一個對象實例,要快速的創建跟它一樣的實例,最簡單的辦法就是複製?這裏又有兩個小的問題

  • 複製誰呢?當然是複製這個對象實例,複製實例的意思是連帶着數據一起復制。
  • 誰來複制呢?應該讓這個類的實例自己來複制,自己複製自己。

       可是每個對象不會那麼聽話,自己去實現複製自己的。於是原型模式決定對這些對象實行強制要求,給這些對象定義一個接口,在接口裏面定義一個方法,這個方法用來要求每個對象實現自己複製自己。

由於現在存在訂單的接口,因此就把這個要求克隆自身的方法定義在訂單的接口裏面,示例代碼如下:

/**

 * 訂單的接口,聲明瞭可以克隆自身的方法

 */

public interface OrderApi {

    public int getOrderProductNum();

    public void setOrderProductNum(int num);

    /**

     * 克隆方法

     * @return 訂單原型的實例

     */

    public OrderApi cloneOrder();

}

(2)如何克隆

定義好了克隆的接口,那麼在訂單的實現類裏面,就得讓它實現這個接口,並具體的實現這個克隆方法,新的問題出來了,如何實現克隆呢?

       很簡單,只要先new一個自己對象的實例,然後把自己實例中的數據取出來,設置到新的對象實例中去,不就可以完成實例的複製了嘛,複製的結果就是有了一個跟自身一模一樣的實例。

       有的朋友可能會說:不用那麼費勁吧,直接返回本實例不就可以了?如下:

public Object clone(){

    return this;

}

請注意了,這是一種典型的錯誤,這麼做,每次克隆,客戶端獲取的其實都是同一個實例,都是指向同一個內存空間的,對克隆出來的對象實例的修改會影響到原型對象實例。

       那麼應該怎麼克隆呢,最基本的做法就是新建一個類實例,然後把所有屬性的值複製到新的實例中,先看看個人訂單對象的實現,示例代碼如下:

/**

 * 個人訂單對象

 */

public class PersonalOrder implements OrderApi{

    private String customerName;

    private String productId;

    private int orderProductNum = 0;

   

    public int getOrderProductNum() {

       return this.orderProductNum;

    }  

    public void setOrderProductNum(int num) {

       this.orderProductNum = num;

    }  

    public String getCustomerName() {

       return customerName;

    }

    public void setCustomerName(String customerName) {

       this.customerName = customerName;

    }

    public String getProductId() {

       return productId;

    }

    public void setProductId(String productId) {

       this.productId = productId;

    }

    public String toString(){

       return "本個人訂單的訂購人是="+this.customerName

+",訂購產品是="+this.productId+",訂購數量爲="

+this.orderProductNum;

    }  

    public OrderApi cloneOrder() {

       //創建一個新的訂單,然後把本實例的數據複製過去

       PersonalOrder order = new PersonalOrder();

       order.setCustomerName(this.customerName);

       order.setProductId(this.productId);

       order.setOrderProductNum(this.orderProductNum);

      

       return order;

    }

}

       接下來看看企業訂單的具體實現,示例代碼如下:

/**

 * 企業訂單對象

 */

public class EnterpriseOrder implements OrderApi{

    private String enterpriseName;

    private String productId;  

    private int orderProductNum = 0;

    public int getOrderProductNum() {

       return this.orderProductNum;

    }  

    public void setOrderProductNum(int num) {

       this.orderProductNum = num;

    }  

    public String getEnterpriseName() {

       return enterpriseName;

    }

    public void setEnterpriseName(String enterpriseName) {

       this.enterpriseName = enterpriseName;

    }

    public String getProductId() {

       return productId;

    }

    public void setProductId(String productId) {

       this.productId = productId;

    }

    public String toString(){

       return "本企業訂單的訂購企業是="+this.enterpriseName

+",訂購產品是="+this.productId+",訂購數量爲="

+this.orderProductNum;

    }

    public OrderApi cloneOrder() {

       //創建一個新的訂單,然後把本實例的數據複製過去

       EnterpriseOrder order = new EnterpriseOrder();

       order.setEnterpriseName(this.enterpriseName);

       order.setProductId(this.productId);

       order.setOrderProductNum(this.orderProductNum);

       return order;

    }  

}

(3)使用克隆方法

這裏使用訂單接口的克隆方法的,是訂單的處理對象,也就是說,訂單的處理對象就相當於原型模式結構中的Client。

當然,客戶端在調用clone方法之前,還需要先獲得相應的實例對象,有了實例對象,才能調用該實例對象的clone方法。

這裏使用克隆方法的時候,跟標準的原型實現有一些不同,在標準的原型實現的示例代碼裏面,客戶端是持有需要克隆的對象,而這裏變化成了通過方法傳入需要使用克隆的對象,這點大家注意一下。示例代碼如下:

public class OrderBusiness {

    /**

     * 創建訂單的方法

     * @param order 訂單的接口對象

     */

    public void saveOrder(OrderApi order){

       //1:判斷當前的預定產品數量是否大於1000

       while(order.getOrderProductNum() > 1000){

           //2:如果大於,還需要繼續拆分

           //2.1再新建一份訂單,跟傳入的訂單除了數量不一樣外,其它都相同

           OrderApi newOrder = order.cloneOrder();

           //然後進行賦值,產品數量爲1000

           newOrder.setOrderProductNum(1000);

          

           //2.2原來的訂單保留,把數量設置成減少1000

           order.setOrderProductNum(

order.getOrderProductNum()-1000);

          

           //然後是業務功能處理,省略了,打印輸出,看一下

           System.out.println("拆分生成訂單=="+newOrder);

       }     

       //3:不超過,那就直接業務功能處理,省略了,打印輸出,看一下

       System.out.println("訂單=="+order);

    }

}

(4)客戶端的測試代碼,跟前面的示例是完全一樣的,這裏就不去贅述了,去運行一下,看看運行的效果,享受一下克隆的樂趣。

在上面的例子中,在訂單處理對象的保存訂單方法裏面的這句話“OrderApi newOrder = order.cloneOrder();”,就用一個訂單的原型實例來指定了對象的種類,然後通過克隆這個原型實例來創建出了一個新的對象實例。

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