天天看點

設計模式:工廠模式

一、引子

話說十年前,有一個爆發戶,他家有三輛汽車(Benz(奔馳)、Bmw(寶馬)、Audi(奧迪)看來這人比較愛國,沒有日本車),還雇了司機為他開車。不過,爆發戶坐車時總是這樣:上Benz車後跟司機說"開奔馳車!",坐上Bmw後他說"開寶馬車!",坐上Audi後他說"開奧迪車!"。你一定說:這人有病!直接說開車不就行了?! 而當把這個爆發戶的行為放到我們程式語言中來,我們發現C語言一直是通過這種方式來坐車的!幸運的是,這種有病的現象在OO語言中可以避免了。下面以Java語言為基礎來引入我們本文的主題:工廠模式!!

二、簡介

工廠模式主要是為建立對象提供了接口。工廠模式按照《Java與模式》中的提法分為三類:

1. 簡單工廠模式(Simple Factory) 

2. 工廠方法模式(Factory Method) 

3. 抽象工廠模式(Abstract Factory) 

這三種模式從上到下逐漸抽象,并且更具一般性。還有一種分類法,就是将簡單工廠模式看為工廠方法模式的一種特例,兩個歸為一類。下面是使用工廠模式的兩種情況:

1.在編碼時不能預見需要建立哪種類的執行個體。

2.系統不應依賴于産品類執行個體如何被建立、組合和表達的細節

三、簡單工廠模式

顧名思義,這個模式本身很簡單,而且使用在業務較簡單的情況下。

它由三種角色組成(關系見下面的類圖):

1、工廠類角色:這是本模式的核心,含有一定的商業邏輯和判斷邏輯。在java中它往往由一個具體類實作。

2、抽象産品角色:它一般是具體産品繼承的父類或者實作的接口。在java中由接口或者抽象類來實作。

3、具體産品角色:工廠類所建立的對象就是此角色的執行個體。在java中由一個具體類實作。

設計模式:工廠模式

那麼簡單工廠模式怎麼用呢?我來舉個例子吧,我想這個比講一大段理論上的文字描述要容易了解的多!下面就來給那個暴發戶治病: P 

在使用了簡單工廠模式後,現在暴發戶隻需要坐在車裡對司機說句:"開車"就可以了。來看看怎麼實作的:

//抽象産品角色      
public interface Car{       
public void drive();       
}      
//具體産品角色      
public class Benz implements Car{       
public void drive() {       
System.out.println("Driving Benz ");       
}       
}      
public class Bmw implements Car{       
public void drive() {       
System.out.println("Driving Bmw ");       
}       
}       
。。。(奧迪我就不寫了:P)      
//工廠類角色      
public class Driver{      
//工廠方法      
//注意 傳回類型為抽象産品角色      
public static Car driverCar(String s)throws Exception {      
//判斷邏輯,傳回具體的産品角色給Client       
if(s.equalsIgnoreCase("Benz")) return new Benz();       
else if(s.equalsIgnoreCase("Bmw"))       
return new Bmw();      
......       
else throw new Exception();       
。。。      
//歡迎暴發戶出場......       
public class Magnate{       
public static void main(String[] args){       
try{       
//告訴司機我今天坐奔馳      
Car car = Driver.driverCar("benz");       
//下指令:開車      
car.drive();       
。。。      

如果将所有的類放在一個檔案中,請不要忘記隻能有一個類被聲明為public。 程式中類之間的關系如下:

設計模式:工廠模式

這便是簡單工廠模式了。下面是其好處:

首先,使用了簡單工廠模式後,我們的程式不在"有病",更加符合現實中的情況;而且用戶端免除了直接建立産品對象的責任,而僅僅負責"消費"産品(正如暴發戶所為)。

下面我們從開閉原則上來分析下簡單工廠模式。當暴發戶增加了一輛車的時候,隻要符合抽象産品制定的合同,那麼隻要通知工廠類知道就可以被客戶使用了。那麼對于産品部分來說,它是符合開閉原則的--對擴充開放、對修改關閉;但是工廠部分好像不太理想,因為每增加一輛車,都要在工廠類中增加相應的商業邏輯和判斷邏輯,這顯自然是違背開閉原則的。

對于這樣的工廠類(在我們的例子中是為司機師傅),我們稱它為全能類或者上帝類。

我們舉的例子是最簡單的情況,而在實際應用中,很可能産品是一個多層次的樹狀結構。由于簡單工廠模式中隻有一個工廠類來對應這些産品,是以這可能會把我們的上帝類壞了,進而累壞了我們可愛的程式員:( 

正如我前面提到的簡單工廠模式适用于業務将簡單的情況下。而對于複雜的業務環境可能不太适應阿。這就應該由工廠方法模式來出場了!!

四、工廠方法模式

先來看下它的組成吧:

1、抽象工廠角色:這是工廠方法模式的核心,它與應用程式無關。是具體工廠角色必須實作的接口或者必須繼承的父類。在java中它由抽象類或者接口來實作。

2、具體工廠角色:它含有和具體業務邏輯有關的代碼。由應用程式調用以建立對應的具體産品的對象。在java中它由具體的類來實作。

3、抽象産品角色:它是具體産品繼承的父類或者是實作的接口。在java中一般有抽象類或者接口來實作。

4、具體産品角色:具體工廠角色所建立的對象就是此角色的執行個體。在java中由具體的類來實作。

來用類圖來清晰的表示下的它們之間的關系:

設計模式:工廠模式

我們還是老規矩使用一個完整的例子來看看工廠模式各個角色之間是如何來協調的。話說暴發戶生意越做越大,自己的愛車也越來越多。這可苦了那位司機師傅了,什麼車它都要記得,維護,都要經過他來使用!于是暴發戶同情他說:看你跟我這麼多年的份上,以後你不用這麼辛苦了,我給你配置設定幾個人手,你隻管管好他們就行了!于是,工廠方法模式的管理出現了。代碼如下:

//抽象産品角色,具體産品角色與簡單工廠模式類似,隻是變得複雜了些,這裡略。

//抽象工廠角色      
public interface Driver{       
public Car driverCar();       
}       
public class BenzDriver implements Driver{       
public Car driverCar(){       
return new Benz();       
}       
}       
public class BmwDriver implements Driver{       
public Car driverCar() {       
return new Bmw();       
}       
}       
......//應該和具體産品形成對應關系,這裡略...       
//有請暴發戶先生      
public class Magnate       
{       
public static void main(String[] args)       
{       
try{       
Driver driver = new BenzDriver();      
Car car = driver.driverCar();       
car.drive();       
}catch(Exception e)       
{ }       
}       
}       

工廠方法使用一個抽象工廠角色作為核心來代替在簡單工廠模式中使用具體類作為核心。讓我們來看看工廠方法模式給我們帶來了什麼?使用開閉原則來分析下工廠方法模式。當有新的産品(即暴發戶的汽車)産生時,隻要按照抽象産品角色、抽象工廠角色提供的合同來生成,那麼就可以被客戶使用,而不必去修改任何已有的代碼。看來,工廠方法模式是完全符合開閉原則的!

使用工廠方法模式足以應付我們可能遇到的大部分業務需求。但是當産品種類非常多時,就會出現大量的與之對應的工廠類,這不應該是我們所希望的。是以我建議在這種情況下使用簡單工廠模式與工廠方法模式相結合的方式來減少工廠類:即對于産品樹上類似的種類(一般是樹的葉子中互為兄弟的)使用簡單工廠模式來實作。

當然特殊的情況,就要特殊對待了:對于系統中存在不同的産品樹,而且産品樹上存在産品族,那麼這種情況下就可能可以使用抽象工廠模式了。

五、小結

讓我們來看看簡單工廠模式、工廠方法模式給我們的啟迪:

如果不使用工廠模式來實作我們的例子,也許代碼會減少很多--隻需要實作已有的車,不使用多态。但是在可維護性上,可擴充性上是非常差的(你可以想象一下,添加一輛車後要牽動的類)。是以為了提高擴充性和維護性,多寫些代碼是值得的。

六、抽象工廠模式

先來認識下什麼是産品族:位于不同産品等級結構中,功能相關聯的産品組成的家族。如果光看這句話就能清楚的了解這個概念,我不得不佩服你啊。還是讓我們用一個例子來形象地說明一下吧。

圖中的BmwCar和BenzCar就是兩個産品樹(産品層次結構);而如圖所示的BenzSportsCar和BmwSportsCar就是一個産品族。他們都可以放到跑車家族中,是以功能有所關聯。同理BmwBussinessCar和BenzSportsCar也是一個産品族。

回到抽象産品模式的話題上,可以這麼說,它和工廠方法模式的差別就在于需要建立對象的複雜程度上。而且抽象工廠模式是三個裡面最為抽象、最具一般性的。抽象工廠模式的用意為:給用戶端提供一個接口,可以建立多個産品族中的産品對象。而且使用抽象工廠模式還要滿足一下條件:

1.系統中有多個産品族,而系統一次隻可能消費其中一族産品

2.同屬于同一個産品族的産品一起使用時。

來看看抽象工廠模式的各個角色(和工廠方法的如出一轍):

抽象工廠角色:這是工廠方法模式的核心,它與應用程式無關。是具體工廠角色必須實作的接口或者必須繼承的父類。在java中它由抽象類或者接口來實作。

具體工廠角色:它含有和具體業務邏輯有關的代碼。由應用程式調用以建立對應的具體産品的對象。在java中它由具體的類來實作。

抽象産品角色:它是具體産品繼承的父類或者是實作的接口。在java中一般有抽象類或者接口來實作。

具體産品角色:具體工廠角色所建立的對象就是此角色的執行個體。在java中由具體的類來實作。

設計模式:工廠模式

看過了前兩個模式,對這個模式各個角色之間的協調情況應該心裡有個數了,我就不舉具體的例子了。隻是一定要注意滿足使用抽象工廠模式的條件哦,不然即使存在了多個産品樹,也存在産品族,但是不能使用的。

簡單工廠模式

1. 目的 

        工廠模式就是專門負責将大量有共同接口的類執行個體化,而且不必事先知道每次是要執行個體化哪一個類的模式。它定義一個用于建立對象的接口,由子類決定執行個體化哪一個類。

2 . 簡單工廠模式的結構 

3. 一個簡單例子

java 代碼

  1. // 産品接口         
  2. public interface Product {   
  3.     public void getName();   
  4. }   
  5. // 具體産品A   
  6. public class ProductA implements Product {   
  7.     public void getName() {   
  8.         System.out.println("  I am ProductA  ");   
  9.     }   
  10. }   
  11. // 具體産品B   
  12. public class ProductB implements Product {   
  13.     public void getName() {   
  14.         System.out.println("  I am ProductB  ");   
  15.     }   
  16. }   
  17. // 工廠類   
  18. public class ProductCreator {   
  19.     public Product createProduct(String type) {   
  20.         if (" A ".equals(type)) {   
  21.             return new ProductA();   
  22.         }   
  23.         if (" B ".equals(type)) {   
  24.             return new ProductB();   
  25.         } else  
  26.             return null;   
  27.     }   
  28.     public static void main(String[] args) {   
  29.         ProductCreator creator = new ProductCreator();   
  30.         creator.createProduct(" A ").getName();   
  31.         creator.createProduct(" B ").getName();   
  32.     }   
  33. }  

4. 小結工廠模式的适用範圍 

• 在編碼時不能預見需要建立哪一種類的執行個體。 

• 一個類使用它的子類來建立對象。 

• 開發人員不希望建立了哪個類的執行個體以及如何建立執行個體的資訊暴露給外部程式。  

抽象工廠模式 

1. 抽象工廠模式可以說是簡單工廠模式的擴充,它們主要的差別在于需要建立對象的複雜程度上。 

在抽象工廠模式中,抽象産品可能是一個或多個,進而構成一個或多個産品族。 在隻有一個産品族的情況下,抽象工廠模式實際上退化到工廠方法模式。 

2. 抽象工廠模式的結構 

3. 一個簡單例子

java 代碼

  1. //  産品 Plant接口         
  2. public interface Plant {   
  3. }   
  4. // 具體産品PlantA,PlantB   
  5. public class PlantA implements Plant {   
  6.     public PlantA() {   
  7.         System.out.println(" create PlantA ! ");   
  8.     }   
  9.     public void doSomething() {   
  10.         System.out.println("  PlantA do something  ");   
  11.     }   
  12. }   
  13. public class PlantB implements Plant {   
  14.     public PlantB() {   
  15.         System.out.println(" create PlantB ! ");   
  16.     }   
  17.     public void doSomething() {   
  18.         System.out.println("  PlantB do something  ");   
  19.     }   
  20. }   
  21. // 産品 Fruit接口   
  22. public interface Fruit {   
  23. }   
  24. // 具體産品FruitA,FruitB   
  25. public class FruitA implements Fruit {   
  26.     public FruitA() {   
  27.         System.out.println(" create FruitA ! ");   
  28.     }   
  29.     public void doSomething() {   
  30.         System.out.println("  FruitA do something  ");   
  31.     }   
  32. }   
  33. public class FruitB implements Fruit {   
  34.     public FruitB() {   
  35.         System.out.println(" create FruitB ! ");   
  36.     }   
  37.     public void doSomething() {   
  38.         System.out.println("  FruitB do something  ");   
  39.     }   
  40. }   
  41. // 抽象工廠方法   
  42. public interface AbstractFactory {   
  43.     public Plant createPlant();   
  44.     public Fruit createFruit();   
  45. }   
  46. // 具體工廠方法   
  47. public class FactoryA implements AbstractFactory {   
  48.     public Plant createPlant() {   
  49.         return new PlantA();   
  50.     }   
  51.     public Fruit createFruit() {   
  52.         return new FruitA();   
  53.     }   
  54. }   
  55. public class FactoryB implements AbstractFactory {   
  56.     public Plant createPlant() {   
  57.         return new PlantB();   
  58.     }   
  59.     public Fruit createFruit() {   
  60.         return new FruitB();   
  61.     }   
  62. }  

4. 小結 

在以下情況下,應當考慮使用抽象工廠模式。 

  首先,一個系統應當不依賴于産品類執行個體被創立,組成,和表示的細節。這對于所有形态的工廠模式都是重要的。 

  其次,這個系統的産品有多于一個的産品族。 

  第三,同屬于同一個産品族的産品是設計成在一起使用的。這一限制必須得在系統的設計中展現出來。 

  最後,不同的産品以一系列的接口的面貌出現,進而使系統不依賴于接口實作的細節。 

  其中第二丶第三個條件是我們選用抽象工廠模式而非其它形态的工廠模式的關鍵性條件。