設計模式(一)原廠模式Factory(建立型)

來源:互聯網
上載者:User

標籤:style   blog   http   java   color   使用   strong   io   



設計模式一 原廠模式Factory

         在物件導向編程中, 最通常的方法是一個new操作符產生一個對象執行個體,new操作符就是用來構造對象執行個體的。可是在一些情況下, new操作符直接產生對象會帶來一些問題。舉例來說, 很多類型對象的創造須要一系列的步驟: 你可能須要計算或取得對象的初始設定; 選擇產生哪個子物件執行個體; 或在產生你須要的對象之前必須先產生一些協助工具功能的對象。 在這些情況,新對象的建立就是一個 “過程”,不僅是一個操作,像一部大機器中的一個齒輪傳動。

模式的問題:你怎樣能輕鬆方便地構造對象執行個體,而不必關心構造對象執行個體的細節和複雜過程呢?

解決方式:建立一個工廠來建立對象。

實現:

一、引言
    1)還沒有工廠時代:假如還沒有工業革命,假設一個客戶要一款寶馬車,一般的做法是客戶去建立一款寶馬車,然後拿來用。
    2)簡單原廠模式:後來出現工業革命。使用者不用去建立寶馬車。由於客戶有一個工廠來幫他建立寶馬.想要什麼車,這個工廠就能夠建。比方想要320i系列車。工廠就建立這個系列的車。即工廠能夠建立產品。
    3)Factory 方法模式時代:為了滿足客戶,寶馬車系列越來越多,如320i,523i,30li等系列一個工廠無法建立全部的寶馬系列。於是由單獨分出來多個詳細的工廠。每一個詳細工廠建立一種系列。即詳細工廠類僅僅能建立一個詳細產品。可是寶馬工廠還是個抽象。你須要指定某個詳細的工廠才幹生產車出來。
    4)抽象原廠模式時代:隨著客戶的要求越來越高,寶馬車必須配置空調。並且這空調必須相應給系列車才幹使用。於是這個工廠開始生產寶馬車和須要的空調。
         終於是客戶僅僅要對寶馬的銷售人員說:我要523i空調車,銷售人員就直接給他523i空調車了。而不用自己去建立523i空調車寶馬車.
   (我僅僅是舉個範例,說到寶馬配置空調全然是為了舉例,甚至有點扯,哪有車和空調必須相應才幹使用啊)
     這就是原廠模式。
二、分類 
        原廠模式主要是為建立對象提供過渡介面,以便將建立對象的詳細過程屏蔽隔離起來,達到提高靈活性的目的。 
原廠模式能夠分為三類: 
1)簡單原廠模式(Simple Factory) 
2)Factory 方法模式(Factory Method) 
3)抽象原廠模式(Abstract Factory) 
         這三種模式從上到下逐步抽象,而且更具一般性。 
        GOF在《設計模式》一書中將原廠模式分為兩類:Factory 方法模式(Factory Method)與抽象原廠模式(Abstract Factory)。將簡單原廠模式(Simple Factory)看為Factory 方法模式的一種特例,兩者歸為一類。 
三、差別 
Factory 方法模式:
一個抽象產品類,能夠派生出多個詳細產品類。   
一個抽象工廠類,能夠派生出多個詳細工廠類。   
每一個詳細工廠類僅僅能建立一個詳細產品類的執行個體。
抽象原廠模式:
多個抽象產品類,每一個抽象產品類能夠派生出多個詳細產品類。   
一個抽象工廠類,能夠派生出多個詳細工廠類。   
每一個詳細工廠類能夠建立多個詳細產品類的執行個體。   
差別:
Factory 方法模式僅僅有一個抽象產品類,而抽象原廠模式有多個。   
Factory 方法模式的詳細工廠類僅僅能建立一個詳細產品類的執行個體,而抽象原廠模式能夠建立多個。
兩者皆可。 


四、簡單原廠模式 
建立一個工廠(一個函數或一個類方法)來製造新的對象。
分布說明引子:從無到有。客戶自己建立寶馬車,然後拿來用。
 


<?php/** * 車子系列 * */Class BWM320{function __construct($pa) {}}Class BMW523{   function __construc($pb){}}/** *  * 客戶自己建立寶馬車 */class Customer {   function createBMW320(){       return new BWM320();   }   function createBMW523(){       return new BMW523();   }} 


       客戶須要知道怎麼去建立一款車,客戶和車就緊密耦合在一起了.為了減少耦合,就出現了工廠類,把建立寶馬的操作細節都放到了工廠裡面去,客戶直接使用工廠的建立Factory 方法,傳入想要的寶馬車型號即可了,而不必去知道建立的細節.這就是工業革命了:簡單原廠模式

即我們建立一個工廠類方法來製造新的對象。



產品類:

<?php/** * 車子系列 * */abstract Class BWM{    function __construct($pa) {    }}Class BWM320 extends BWM{    function __construct($pa) {    }}Class BMW523 extends BWM{   function __construc($pb){   }}

 工廠類:

/** *  * 工廠建立車 */class Factory {    static function  createBMW($type){        switch ($type) {          case 320:             return new BWM320();          case 523:             return new BMW523();        //....   }}

客戶類:

/** *  * 客戶通過工廠擷取車 */class Customer {    private $BMW;    function getBMW($type){        $this?-> BMW =  Factory::createBMW($type);    }}


      簡單原廠模式又稱靜態Factory 方法模式。重新命名上就能夠看出這個模式一定非常easy。它存在的目的非常easy:定義一個用於建立對象的介面。 
      先來看看它的組成: 
         1) 工廠類角色:這是本模式的核心,含有一定的商業邏輯和推斷邏輯。
         2) 抽象產品角色:它通常是詳細產品繼承的父類或者實現的介面。         
         3) 詳細產品角色:工廠類所建立的對象就是此角色的執行個體。在java中由一個詳細類實現。 
        
        以下我們從開閉原則(對擴充開放;對改動封閉)上來分析下簡單原廠模式。當客戶不再滿足現有的車型號的時候,想要一種速度快的新型車,僅僅要這種車符合抽象產品制定的合約,那麼僅僅要通知工廠類知道就能夠被客戶使用了。所以對產品部分來說,它是符合開閉原則的;可是工厂部分好像不太理想,由於每添加一種新型車,都要在工廠類中添加對應的建立商務邏輯(createBMW($type)方法須要新增case),這顯然是違背開閉原則的。可想而知對於新產品的添加,工廠類是非常被動的。對於這種工廠類,我們稱它為全能類 或者上帝類。 
        我們舉的範例是最簡單的情況,而在實際應用中,非常可能產品是一個多層次的樹狀結構。因為簡單原廠模式中僅僅有一個工廠類來相應這些產品,所以這可能會把我們的上帝很累了,也很累了我們這些程式猿:( 
        於是Factory 方法模式作為救世主出現了。 工廠類定義成了介面,而每新增的車種類型,就添加該車種類型相應工廠類的實現,這樣工廠的設計就能夠擴充了,而不必去改動原來的代碼。
五、Factory 方法模式 
        Factory 方法模式去掉了簡單原廠模式中Factory 方法的靜態屬性,使得它能夠被子類繼承。這樣在簡單原廠模式裡集中在Factory 方法上的壓力能夠由Factory 方法模式裡不同的工廠子類來分擔。 
Factory 方法模式組成: 
       1)抽象工廠角色: 這是Factory 方法模式的核心,它與應用程式無關。是詳細工廠角色必須實現的介面或者必須繼承的父類。在java中它由抽象類別或者介面來實現。 
       2)詳細工廠角色:它含有和詳細商務邏輯有關的代碼。由應用程式調用以建立相應的詳細產品的對象。 
       3)抽象產品角色:它是詳細產品繼承的父類或者是實現的介面。在java中一般有抽象類別或者介面來實現。 
       4)詳細產品角色:詳細工廠角色所建立的對象就是此角色的執行個體。在java中由詳細的類來實現。 
       Factory 方法模式使用繼承自抽象工廠角色的多個子類來取代簡單原廠模式中的“上帝類”。正如上面所說,這樣便分擔了對象承受的壓力;並且這樣使得結構變得靈活 起來——當有新的產品產生時,僅僅要依照抽象產品角色、抽象工廠角色提供的合約來產生,那麼就能夠被客戶使用,而不必去改動不論什麼已有 的代碼。能夠看出工廠角色的結構也是符合開閉原則的! 
 


代碼例如以下: 

產品類:

<?php/** * 車子系列 * */abstract Class BWM{function __construct($pa) {}}Class BWM320 extends BWM{function __construct($pa) {}}Class BMW523 extends BWM{   function __construc($pb){}}


建立工廠類:

/** * 建立工廠的介面 * */interface FactoryBMW {        function createBMW(); } /** *  * 建立BWM320車 */class FactoryBWM320 implements FactoryBMW {   function  createBMW($type){      return new BWM320();   }}/** *  * 建立BWM523車 */class FactoryBWM523 implements FactoryBMW {   function  createBMW($type){      return new BMW523();   }}


客戶類:

/** *  * 客戶得到車 */class Customer {   private $BMW;   function  getBMW($type){      switch ($type) {        case 320:           $BWM320 = new FactoryBWM320();           return $BWM320->createBMW();        case 523:           $BWM523 = new FactoryBWM523();           return $BWM320->createBMW();            //....      }  }}


       能夠看出Factory 方法的增加,使得對象的數量成倍增長。當產品種類許多時,會出現大量的與之相應的工廠對象,這不是我們所希望的。由於假設不能避免這樣的情 況,能夠考慮使用簡單原廠模式與Factory 方法模式相結合的方式來降低工廠類:即對於產品樹上類似的種類(通常是樹的葉子中互為兄弟的)使用簡單原廠模式來實 現。

Factory 方法小結:
        Factory 方法模式彷彿已經非常完美的對對象的建立進行了封裝,使得客戶程式中只處理抽象產品角色提供的介面。那我們是否一定要在代碼中遍布工廠呢?大可不必。或許在以下情況下你能夠考慮使用Factory 方法模式: 
     1)當客戶程式不須要知道要使用對象的建立過程。 
     2)客戶程式使用的對象存在變動的可能,或者根本就不知道使用哪一個詳細的對象。


       簡單原廠模式與Factory 方法模式真正的避免了代碼的改動了?沒有。在簡單原廠模式中,新產品的增加要改動工廠角色中的推斷語句;而在Factory 方法模式中,要麼將判 斷邏輯留在抽象工廠角色中,要麼在客戶程式中將詳細工廠角色寫死(就象上面的範例一樣)。並且產品對象建立條件的改變必定會引起工廠角色的改動。
       面對這樣的情況,我們能夠使用反射機制:

 class Customer {     private $BMW;     function  getBMW($type){         $class = new ReflectionClass(‘FactoryBWM‘ .$type );//建立 ‘FactoryBWM‘這個類的反射類            $instance  = $class->newInstanceArgs();//相當於執行個體化‘FactoryBWM‘ .$type類            return $instance->createBMW();        //或者直接          /**         * $instance = new ‘FactoryBWM‘ .$type();         * return $instance->createBMW();         */    }}

 

六、抽象原廠模式 
       隨著客戶的要求越來越高,寶馬車須要配置空調。於是這個工廠開始生產寶馬車和配置須要的空調。這時候工廠有二個系列的產品:寶馬車和空調.寶馬車必須使用相應的空調才幹使用.這時候分別使用一個車工廠和一個空調工廠都不能滿足我們的需求,我們必須確認車跟空調的相應關係。因此把車工廠跟空調工廠聯絡在一起。因此出現了抽象原廠模式。
     能夠說,抽象原廠模式和Factory 方法模式的差別就在於須要建立對象的複雜程度上。並且抽象原廠模式是三個裡面最為抽象、最具一般性的。
抽象原廠模式的用意為:給client提供一個介面,能夠建立多個產品族中的產品對象 ,並且使用抽象原廠模式還要滿足一下條件:
     1)系統中有多個產品族,而系統一次僅僅可能消費當中一族產品。
     2)同屬於同一個產品族的產品以其使用。
抽象原廠模式的各個角色(和Factory 方法一樣):
     1)抽象工廠角色: 這是Factory 方法模式的核心,它與應用程式無關。是詳細工廠角色必須實現的介面或者必須繼承的父類。在java中它由抽象類別或者介面來實現。 
     2)詳細工廠角色:它含有和詳細商務邏輯有關的代碼。由應用程式調用以建立相應的詳細產品的對象。
     3)抽象產品角色:它是詳細產品繼承的父類或者是實現的介面。
     4)詳細產品角色:詳細工廠角色所建立的對象就是此角色的執行個體。

 

其結構:


 
我們的範例:
 

代碼:

產品類:

<?php/** * 車子系列以及型號 * */abstract class  BWM{}class BWM523 extends  BWM {}class BWM320 extends  BWM {}/** * 空調 * */abstract class aircondition{}class airconditionBWM320  extends aircondition {}class airconditionBWM52 extends aircondition {}
建立工廠類:

/** * 建立工廠的介面 * */interface FactoryBMW {      function createBMW();      function createAirC(); } /** *  * 建立BWM320車 */class FactoryBWM320 implements FactoryBMW {    function  createBMW(){    return new BWM320();}function  createAirC(){ //空調    return new airconditionBWM320();}}/** *  * 建立BWM523車 */class FactoryBWM523 implements FactoryBMW {    function  createBMW(){    return new BWM523();}function  createAirC(){    return new airconditionBWM523();}}
客戶:

/** *  * 客戶得到車 */class Customer {   private $BMW;   private $airC;   function  getBMW($type){       $class = new ReflectionClass(‘FactoryBWM‘ .$type );//建立 Person這個類的反射類          $instance  = $class->newInstanceArgs();//相當於執行個體化Person 類          $this->BMW =  $instance->createBMW();       $this->airC =  $instance->createAirC();   }}


聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.