設計模式(四)原型模式Prototype(創建型)

1.   概述

我們都知道,創建型模式一般是用來創建一個新的對象,然后我們使用這個對象完成一些對象的操作,我們通過原型模式可以快速的創建一個對象而不需要提供專門的new()操作就可以快速完成對象的創建,這無疑是一種非常有效的方式,快速的創建一個新的對象。

例子1:孫悟空拔下一嘬猴毛,輕輕一吹就會變出好多的孫悟空來。

例子2:寄個快遞
下面是一個郵寄快遞的場景:
“給我寄個快遞。”顧客說。
“寄往什么地方?寄給……?”你問。
“和上次差不多一樣,只是郵寄給另外一個地址,這里是郵寄地址……”顧客一邊說一邊把寫有郵寄地址的紙條給你。
“好!”你愉快地答應,因為你保存了用戶的以前郵寄信息,只要復制這些數據,然后通過簡單的修改就可以快速地創建新的快遞數據了。

2. 問題

當對象的構造函數非常復雜,在生成新對象的時候非常耗時間、耗資源的情況?我們是怎么來創建呢?

3. 解決方案

       通過復制(克隆、拷貝)一個指定類型的對象來創建更多同類型的對象。這個指定的對象可被稱為“原型”對象,也就是通過復制原型對象來得到更多同類型的對象。即原型設計模式。在php的很多模板庫,都用到clone。如smarty等。

4. 適用性

原型模式的主要思想是基于現有的對象克隆一個新的對象出來,一般是有對象的內部提供克隆的方法,通過該方法返回一個對象的副本,這種創建對象的方式,相比我們之前說的幾類創建型模式還是有區別的,之前的講述的工廠模式與抽象工廠都是通過工廠封裝具體的new操作的過程,返回一個新的對象,有的時候我們通過這樣的創建工廠創建對象不值得,特別是以下的幾個場景的時候,可能使用原型模式更簡單也效率更高。

? 1)當一個系統應該獨立于它的產品創建、構成和表示時,要使用 Prototype模式

? 2)當要實例化的類是在運行時刻指定時,例如,通過動態裝載;

? 3)為了避免創建一個與產品類層次平行的工廠類層次時

? 4)當一個類的實例只能有幾個不同狀態組合中的一種時。建立相應數目的原型并克隆它們可能比每次用合適的狀態手工實例化該類更方便一些。(也就是當我們在處理一些對象比較簡單,并且對象之間的區別很小,可能只是很固定的幾個屬性不同的時候,可能我們使用原型模式更合適)。

5. 結構

     原型模式結構如下頁上圖所示:

       1.gif

6. 組成

客戶(Client)角色:使用原型對象的客戶程序
抽象原型(Prototype)角色:規定了具體原型對象必須實現的接口(如果要提供深拷貝,則必須具有實現clone的規定)

具體原型(ConcretePrototype):從抽象原型派生而來,是客戶程序使用的對象,即被復制的對象。此角色需要實現抽象原型角色所要求的接口。

7. 效果

Prototype模式有許多和Abstract Factory模式 和 Builder模式一樣的效果:它對客戶隱藏了具體的產品類,因此減少了客戶知道的名字的數目。此外,這些模式使客戶無需改變即可使用與特定應用相關的類。
下面列出Prototype模式的另外一些優點。
1 ) 運行時刻增加和刪除產品: Prototype允許只通過客戶注冊原型實例就可以將一個新的具體產品類并入系統。它比其他創建型模式更為靈活,因為客戶可以在運行時刻建立和刪除原型。
2 ) 改變值以指定新對象: 高度動態的系統允許你通過對象復合定義新的行為—例如,通過為一個對象變量指定值—并且不定義新的類。你通過實例化已有類并且將這些實例注冊為客戶對象的原型,就可以有效定義新類別的對象。客戶可以將職責代理給原型,從而表現出新的行為。這種設計使得用戶無需編程即可定義新“類” 。實際上,克隆一個原型類似于實例化一個類。Prototype模式可以極大的減少系統所需要的類的數目。
3) 改變結構以指定新對象:許多應用由部件和子部件來創建對象。
4) 減少子類的構造 Factory Method 經常產生一個與產品類層次平行的 Creator類層次。Prototype模式使得你克隆一個原型而不是請求一個工廠方法去產生一個新的對象。因此你根本不需要Creator類層次。這一優點主要適用于像 C + +這樣不將類作為一級類對象的語言。像Smalltalk和Objective C這樣的語言從中獲益較少,因為你總是可以用一個類對象作為生成者。在這些語言中,類對象已經起到原型一樣的作用了。
5) 用類動態配置應用 一些運行時刻環境允許你動態將類裝載到應用中。在像 C + +這樣的語言中,Prototype模式是利用這種功能的關鍵。一個希望創建動態載入類的實例的應用不能靜態引用類的構造器。而應該由運行環境在載入時自動創建每個類的實例,并用原型管理器來注冊這個實例(參見實現一節) 。這樣應用就可以向原型管理器請求新裝載的類的實例,這些類原本并沒有和程序相連接。 E T + +應用框架[ W G M 8 8 ]有一個運行系統就是使用這一方案的。

Prototype的主要缺陷是每一個Prototype的子類都必須實現clone操作,這可能很困難。

如,當所考慮的類已經存在時就難以新增 clone操作。當內部包括一些不支持拷貝或有循環引用的對象時,實現克隆可能也會很困難的。

8. 實現

<?php  
/** 
 * 原型模式  
 */  
   
/** 
 * 抽象原型角色 
 */  
interface Prototype {  
    public function copy();  
}  
   
/** 
 * 具體原型角色 
 */  
class ConcretePrototype implements Prototype{  
   
    private  $_name;  
   
    public function __construct($name) {  
        $this->_name = $name;  
    }  
   
    public function setName($name) {  
        $this->_name = $name;  
    }  
   
    public function getName() {  
        return $this->_name;  
    }  
   
    public function copy() {  
       /** 深拷貝 */  
       return  clone  $this;      
       /** 淺拷貝 */  
       //return  $this;     
    }  
}  
  
   
class Client {  
   
     /** 
     * Main program. 
     */  
    public static function main() {  
        $object1 = new ConcretePrototype(11);  
        $object_copy = $object1->copy();  
  
        var_dump($object1->getName());  
        echo '<br />';  
        var_dump($object_copy->getName());  
        echo '<br />';  
   
        $object1->setName(22);  
        var_dump($object1->getName());  
        echo '<br />';  
        var_dump($object_copy->getName());  
        echo '<br />';  
    }  
}  
   
Client::main();  
?>

9. 淺拷貝和深拷貝

原型模式的原理圖:

3.png

淺拷貝

被拷貝對象的所有變量都含有與原對象相同的值,而且對其他對象的引用仍然是指向原來的對象。即淺拷貝只負責當前對象實例,對引用的對象不做拷貝。

淺復制后的對象和對象副本的情況:

設計模式(四)原型模式Prototype(創建型)

深拷貝
被拷貝對象的所有的變量都含有與原來對象相同的值,除了那些引用其他對象的變量。那些引用其他對象的變量將指向一個被拷貝的新對象,而不再是原有那些被引用對象。即 深拷貝把要拷貝的對象所引用的對象也都拷貝了一次,而這種對被引用到的對象拷貝叫做間接拷貝。

深復制的對象和對象副本的情況:

4.jpg

深拷貝要深入到多少層,是一個不確定的問題。
在決定以深拷貝的方式拷貝一個對象的時候,必須決定對間接拷貝的對象是采取淺拷貝還是深拷貝還是繼續采用深拷貝。
因此,在采取深拷貝時,需要決定多深才算深。此外,在深拷貝的過程中,很可能會出現循環引用的問題。

10. 帶Prototype Manager的原型模式

     原型模式的第二種形式是帶原型管理器的原型模式,其UML圖如下:

     5.jpg

       原型管理器(Prototype Manager)角色:創建具體原型類的對象,并記錄每一個被創建的對象。

       下面這個例子演示了在原型管理器中存儲用戶預先定義的顏色原型,客戶通過原型管理器克隆顏色對象。

<?php  
/** 
 * abstract Prototype 
 * 
 */  
abstract class ColorPrototype  
{  
  //Methods  
    abstract function  copy();  
}  
  
/** 
 * Concrete Prototype 
 * 
 */  
class Color extends ColorPrototype{  
    //Fields  
    private  $red;  
    private  $green;  
    private  $blue;  
    //Constructors  
    function __construct( $red, $green, $red) {  
        $this->red = $red;  
        $this->green = $green;  
        $this->blue = $red;  
     }  
     /** 
      * set red 
      * 
      * @param unknown_type $red 
      */  
    public  function setRed($red) {  
        $this->red = $red;  
     }  
       
     /** 
      * get red 
      * 
      */  
    public  function getRed(){  
        return  $this->red;  
     }  
     /** 
      *set Green 
      * 
      * @param  $green 
      */  
    public  function setGreen($green) {  
        $this->green = $green;  
     }  
     /** 
      * get Green 
      * 
      * @return unknown 
      */  
    public  function getGreen() {  
        return  $this->green ;  
     }  
     /** 
      *set Blue 
      * 
      * @param  $Blue 
      */  
    public  function setBlue($Blue) {  
        $this->blue = $Blue;  
    }  
     /** 
      * get Blue 
      * 
      * @return unknown 
      */  
    public  function getBlue() {  
        return  $this->blue ;  
    }  
      
    /** 
     * Enter description here... 
     * 
     * @return unknown 
     */  
    function copy(){  
        return clone $this;  
    }  
      
    function display() {  
        echo $this->red , ',', $this->green, ',', $this->blue ,'<br>';  
    }  
}  
/** 
 * Enter description here... 
 * 
 */  
class ColorManager  
{  
    // Fields  
    static  $colors = array();  
    // Indexers  
    public static function add($name, $value){  
        self::$colors[$name] = $value;  
    }  
      
    public static function getCopy($name) {  
        return   self::$colors[$name]->copy();  
    }  
}  
/** 
 *Client 
 * 
 */  
class Client  
{  
    public static function  Main()  
    {  
        //原型:白色  
        ColorManager::add("white", new Color( 255, 0, 0 ));  
          
        //紅色可以由原型白色對象得到,只是重新修改白色: r  
        $red = ColorManager::getCopy('white');  
        $red->setRed(255);  
        $red->display();  
          
        //綠色可以由原型白色對象得到,只是重新修改白色: g  
        $green = ColorManager::getCopy('white');  
        $green->setGreen(255);  
        $green->display();  
          
        //綠色可以由原型白色對象得到,只是重新修改白色: b  
        $Blue = ColorManager::getCopy('white');  
        $Blue->setBlue(255);  
        $Blue->display();  
    }  
}  
  
ini_set('display_errors', 'On');  
error_reporting(E_ALL & ~ E_DEPRECATED);  
Client::Main();  
?>

轉自:http://blog.csdn.net/hguisu/article/details/7518947

原創文章,作者:s19930811,如若轉載,請注明出處:http://www.www58058.com/2945

(0)
s19930811s19930811
上一篇 2015-06-25
下一篇 2015-06-26

相關推薦

  • 第十二周作業

    1、 請描述一次完整的http請求處理過程;  HTTP:超文本傳輸協議 完整的http請求過程 1)、建立TCP/IP連接:經過3次握手,建立連接或拒絕。 2)、瀏覽器向服務器發送HTTP請求。 3)、瀏覽器發送請求頭信息。 4)、服務器應答:服務器接受請求后,會回送應答。 5)、服務器發送應答頭信息。 6)、服務器向瀏覽器發送數據。 7)、服務…

    2017-05-17
  • 馬哥教育網絡班20期+第七周博客作業

    1、創建一個10G分區,并格式為ext4文件系統     (1) 要求其block大小為2048,預留空間百分比為2,卷標為MYDATA,默認掛載屬性包含acl fdisk /dev/sdbnp1110Gw[root@llww3317 ~]# mke2fs -t ext…

    Linux干貨 2016-08-02
  • 第8天:腳本基礎,btrf,raid,

    http://note.youdao.com/yws/public/redirect/share?id=61b38adef24ced4dc646a5ef8e68df18&type=false

    Linux干貨 2016-08-16
  • 第一次發微博

    第一次申請微博,第一次發微博,覺得很難

    Linux干貨 2017-01-06
  • CollabNetSubversionEdge服務搭建

    準備目錄并設置權限 [root@localhost ~]# mkdir /var/svn [root@localhost ~]# chown bohui:bohui /var/svn/ [root@localhost ~]# chmod 700 /v…

    Linux干貨 2015-10-18
  • 文件的權限詳解(二)ACL篇

    文件的權限詳解(二)ACL篇 ACL訪問控制列表作用: 1、 ACL:Access Control List,實現靈活的權限管理2、 除了文件的所有者,所屬組和其它人,可以對更多的用戶設置權限3、 CentOS7.0默認創建的xfs和ext4文件系統有ACL功能。4、 CentOS7.X之前版本,默認手工創建的ext4文件系統無ACL功能。需手動增加: tu…

    Linux干貨 2016-08-05
欧美性久久久久