1. 程式人生 > >設計模式(一)工廠模式Factory(建立型)

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

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

模式的問題:你如何能輕鬆方便地構造物件例項,而不必關心構造物件例項的細節和複雜過程呢?

解決方案建立一個工廠來建立物件

實現:

一、引言
    1)還沒有工廠時代:假如還沒有工業革命,如果一個客戶要一款寶馬車,一般的做法是客戶去建立一款寶馬車,然後拿來用。
    2)簡單工廠模式:後來出現工業革命。使用者不用去建立寶馬車。因為客戶有一個工廠來幫他建立寶馬.想要什麼車,這個工廠就可以建。比如想要320i系列車。工廠就建立這個系列的車。即工廠可以建立產品。
    3)工廠方法模式時代:為了滿足客戶,寶馬車系列越來越多,如320i,523i,30li等系列一個工廠無法建立所有的寶馬系列。於是由單獨分出來多個具體的工廠。每個具體工廠建立一種系列。即具體工廠類只能建立一個具體產品。但是寶馬工廠還是個抽象。你需要指定某個具體的工廠才能生產車出來。
    4)抽象工廠模式時代:隨著客戶的要求越來越高,寶馬車必須配置空調。而且這空調必須對應給系列車才能使用。於是這個工廠開始生產寶馬車和需要的空調。
         最終是客戶只要對寶馬的銷售員說:我要523i空調車,銷售員就直接給他523i空調車了。而不用自己去建立523i空調車寶馬車.
   (我只是舉個例子,說到寶馬配置空調完全是為了舉例,甚至有點扯,哪有車和空調必須對應才能使用啊)
     這就是工廠模式。
二、分類

 
        工廠模式主要是為建立物件提供過渡介面,以便將建立物件的具體過程遮蔽隔離起來,達到提高靈活性的目的。 
工廠模式可以分為三類: 
1)簡單工廠模式(Simple Factory) 
2)工廠方法模式(Factory Method) 
3)抽象工廠模式(Abstract Factory) 
         這三種模式從上到下逐步抽象,並且更具一般性。 
        GOF在《設計模式》一書中將工廠模式分為兩類:工廠方法模式(Factory Method)與抽象工廠模式(Abstract Factory)。將簡單工廠模式(Simple Factory)看為工廠方法模式的一種特例,兩者歸為一類。 
三、區別 
工廠方法模式:
一個抽象產品類,可以派生出多個具體產品類。   
一個抽象工廠類,可以派生出多個具體工廠類。   
每個具體工廠類只能建立一個具體產品類的例項。
抽象工廠模式:
多個抽象產品類,每個抽象產品類可以派生出多個具體產品類。   
一個抽象工廠類,可以派生出多個具體工廠類。   
每個具體工廠類可以建立多個具體產品類的例項。   
區別:
工廠方法模式只有一個抽象產品類,而抽象工廠模式有多個。   
工廠方法模式的具體工廠類只能建立一個具體產品類的例項,而抽象工廠模式可以建立多個。
兩者皆可。 


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




<?php
/**
 * 車子系列
 *
 */
Class BWM320{
function __construct($pa) {

}
}
Class BMW523{
   function __construc($pb){

}
}

/**
 * 
 * 客戶自己建立寶馬車
 */
class Customer {

   function createBMW320(){
       return new BWM320();
   }

   function createBMW523(){
       return new BMW523();
   }
} 


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

即我們建立一個工廠類方法來製造新的物件。如圖:



產品類:

<?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);
    }
}


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


程式碼如下: 

產品類:

<?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();
            //....
      }

  }
}


       可以看出工廠方法的加入,使得物件的數量成倍增長。當產品種類非常多時,會出現大量的與之對應的工廠物件,這不是我們所希望的。因為如果不能避免這種情 況,可以考慮使用簡單工廠模式與工廠方法模式相結合的方式來減少工廠類:即對於產品樹上類似的種類(一般是樹的葉子中互為兄弟的)使用簡單工廠模式來實 現。

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


       簡單工廠模式與工廠方法模式真正的避免了程式碼的改動了?沒有。在簡單工廠模式中,新產品的加入要修改工廠角色中的判斷語句;而在工廠方法模式中,要麼將判 斷邏輯留在抽象工廠角色中,要麼在客戶程式中將具體工廠角色寫死(就象上面的例子一樣)。而且產品物件建立條件的改變必然會引起工廠角色的修改。
       面對這種情況,我們可以使用反射機制:

 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();
         */
    }
}

六、抽象工廠模式 
       隨著客戶的要求越來越高,寶馬車需要配置空調。於是這個工廠開始生產寶馬車和配置需要的空調。這時候工廠有二個系列的產品:寶馬車和空調.寶馬車必須使用對應的空調才能使用.這時候分別使用一個車工廠和一個空調工廠都不能滿足我們的需求,我們必須確認車跟空調的對應關係。因此把車工廠跟空調工廠聯絡在一起。因此出現了抽象工廠模式。
     可以說,抽象工廠模式和工廠方法模式的區別就在於需要建立物件的複雜程度上。而且抽象工廠模式是三個裡面最為抽象、最具一般性的。
抽象工廠模式的用意為:給客戶端提供一個介面,可以建立多個產品族中的產品物件 ,而且使用抽象工廠模式還要滿足一下條件:
     1)系統中有多個產品族,而系統一次只可能消費其中一族產品。
     2)同屬於同一個產品族的產品以其使用。
抽象工廠模式的各個角色(和工廠方法一樣):
     1)抽象工廠角色: 這是工廠方法模式的核心,它與應用程式無關。是具體工廠角色必須實現的介面或者必須繼承的父類。在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();
   }
}