1. 程式人生 > >設計模式4-工廠方法模式(FactoryMethod)、抽象工廠模式(AbstractFactory)解析+案例實踐+總結

設計模式4-工廠方法模式(FactoryMethod)、抽象工廠模式(AbstractFactory)解析+案例實踐+總結

本文是對面向物件設計模式--工廠方法模式(FactoryMethod)和抽象工廠模式(AbstractFactory)的解析,主要分為定義解析、簡單工廠方法講解、工廠方法模式講解、抽象工廠模式講解、多案例練習加深對工廠方法模式與抽象工廠模式的理解、最後總結知識要點。

第一篇:定義解析

工廠方法模式和抽象工廠模式是GoF四人幫整理的《設計模式-可複用面向物件軟體基礎》一書中23種設計模式中歸類為建立型模式中的設計模式,23種設計模式根據它們的用途分為三大類:5種建立型模式、7種結構型模式、11種行為型模式。

引自GoF四人幫對工廠方法模式(Factory Method)的定義與類圖:

定義一個用於建立物件的介面,讓子類決定將哪個子類例項化。Factory Method使一個類的例項化延遲到其子類。

解析:定義一個用於建立物件的介面,讓子類決定將哪個子類例項化;工廠方法模式其作用就是用於建立物件的,我們將要建立的物件稱為產品,建立物件的方法稱為工廠方法,工廠方法模式就像一個工廠生產產品一樣;

一個產品可能會有許多不同的子類產品,如一個產品有不同品牌的產品,在同一個品牌的產品中又會分為不同價格檔次的產品,如果一個產品有3個品牌系列,每個品牌內的產品又分為4個不同的價格檔次的產品,這樣具體的產品子類將會有3*4=12個具體的子產品,如果全部把這些具體的子類的例項化都由一個類或一個方法中決定,這會讓系統缺乏彈性,這個方法依賴的子類太多,一旦需要新增新的子類或刪除某個子類都需要重新開啟該方法,然後進行修改。

工廠方法模式把建立物件的方法工廠方法進行抽象,定義為一個介面,讓子工廠類來實現具體建立產品的工廠方法,如每一個品牌由一個對應品牌子工廠類,由其建立不同計費檔次的產品物件,如果有3個品牌,每個品牌有4個價格檔次的產品,只需要3個子工廠類,每個子工廠類都建立對應品牌的不同價格檔次的產品。當有新的品牌加入時,只需要實現一個新的子工廠類即可。

工廠方法模式使得一個類的例項延遲到其子類;通過利用面向介面程式設計,客戶只需要指定具體的子工廠類和具體產品型別標識,即可使用抽象的工廠方法和抽象的產品建立具體的子產品物件,產品物件的例項化工作由指定的具體子工廠類中實現。

抽象工廠模式(Abstract Factory):提供一個建立一系列相關或相互依賴物件的介面,而無需指定它們具體的類。

解析:提供一個建立一系列相關或相互依賴物件的介面,而無需指定它們具體的類;抽象工廠模式從定義上看,其目的是用於建立一系列相關的物件的,也就是用於建立一個產品族的模式;

如組裝一臺電腦會遇到不同品牌的主機板,需要相應品牌的晶片才能進行匹配,如選擇Intel的主機板就必須使用Intel的CPU、顯示卡等,如果選擇的是AMD的主機板就必須使用AMD的CPU、顯示卡等。主機板、CPU、顯示卡這些屬於不同的產品,它們存在相互依賴的關係,屬於同一個產品族。為了方便建立在一個產品族中的產品,把建立這一系列物件的介面放在同一個抽象工廠中,由具體子產品族抽象工廠實現類來決定建立的產品物件,如Intel的抽象工廠實現類建立Intel的主機板、CPU、顯示卡等,AMD的抽象工廠實現類建立AMD的主機板、CPU、顯示卡等。

第二篇:簡單工廠方法講解

基礎需求:有一個披薩店,有多種不同的披薩,有CheesePizza(乳酪披薩)、GreekPizza(希臘披薩)、PepperoniPizza(臘腸披薩)等。

每一種披薩的製作過程都有如下步驟:

1)做一些準備工作,如麵糰、醬料、芝士等。2)烘烤 3)切片4)裝盒

現在需要為披薩店實現一個生成披薩的系統。

第一次實現嘗試:直接建立披薩的具體實現

Pizza orderPizza(String type){
  Pizza pizza;
  //根據披薩的型別,例項化具體的型別。
  if(type.equals("cheese")){
     pizza = new CheesePizza(); //實現Pizza介面
  }else if(type.equals("greek")){
     pizza = new GreekPizza();
  }else if(type.equals("pepperoni")){
     pizza = new PepperoniPizza():
  }
  //烘烤披薩的準備(擀麵皮、加佐料)
  pizza.prepare();
  pizza.bake(); //烘烤
  pizza.cut(); //切片
  pizza.box(); //裝盒
  return pizza;
}

第一次需求變更:現在我們需要根據不同型別披薩的銷售情況,將銷量不好的披薩下架,同時將新風味的披薩上線。我們需要去掉GreekPizza並增加ClamPizza(蛤蜊披薩)和VeggiePizza(素食披薩)。

根據需求變更修改實現:

Pizza orderPizza(String type){
  Pizza pizza;
  //隨著時間的變化,披薩種類會發生變化
  if(type.equals("cheese")){
     pizza = new CheesePizza(); //實現Pizza介面
  } else if(type.equals("pepperoni")){
     pizza = new PepperoniPizza():
  } else if(type.equals("clam")){
     pizza = new ClamPizza();
  }else if(type.equals("veggie"){
     pizza = new ViggePizza();
  }
  //下面是我們不想要改變的地方。只有發生這些動作的披薩會改變。
  pizza.prepare(); //烘烤披薩的準備(擀麵皮、加佐料)
  pizza.bake(); //烘烤
  pizza.cut(); //切片
  pizza.box(); //裝盒
  return pizza;
}

缺點:1、程式碼綁著具體類,造成緊耦合,會導致程式碼更脆弱、更缺乏彈性。

2、一旦有擴充套件或變化,就需要重新開啟這段程式碼進行檢查和修改。通常這樣修改過的程式碼將會造成部分系統更難維護和更新,而且更容易犯錯。

優化方法:封裝變化原則,找出會變化的部分,把它們從不變化的部分分離出來。

第二次實現嘗試:簡單工廠方法

public class SimplePizzaFactory{
 public static Pizza createPizza(String type){
  Pizza pizza;
  if(type.equals("cheese")){
     pizza = new CheesePizza(); //實現Pizza介面
  } else if(type.equals("pepperoni")){
     pizza = new PepperoniPizza():
  } else if(type.equals("clam")){
    pizza = new ClamPizza();
  }else if(type.equals("veggie"){
    pizza = new ViggePizza();
  }
  return pizza;
 }
}

簡單工廠:將建立物件的程式碼提取到一個單獨的類中,把建立物件的邏輯封裝起來並提供一個簡單的對外介面,稱為簡單工廠。簡單工廠並不是一種設計模式,只是一種程式設計習慣。

PizzaStore是Pizza工廠的客戶,它通過SimplePizzaFactory取得披薩的例項。

SimplePizzaFactory是建立披薩的工廠,createPizza()一般為靜態的。

Pizza是我們的產品,定義為抽象類,具有一些有用的實現,這些實現可以被覆蓋,如具體的披薩類ClamPizza等。

第三篇:工廠方法模式講解

第二次需求變更:現在有幾個來在不同地方的披薩店想要加盟我們的披薩店,有來自紐約、芝加哥、加州等地的披薩店。1)我們希望讓所有披薩店的製作流程是一樣的。 2)各地的加盟店想要在製作流程中加入自己的特色。

需求分析:製作流程是一樣的,即統一的披薩超類。各地加盟商要有自己的特色,即每一個加盟商都要有各自對應的披薩類。我們希望建立一個框架,把加盟店和建立披薩捆綁在一起的同時又要保持一定的彈性。

採用簡單工廠方法實現:

public class SimplePizzaFactory{
 public Pizza createPizza(String type){
  Pizza pizza;
  if(type.equals("NYcheese")){
     pizza = new NYCheesePizza(); //實現Pizza介面
  } else if(type.equals("NYpepperoni")){
     pizza = new NYPepperoniPizza():
  } else if(type.equals("NYclam")){
     pizza = new NYClamPizza();
  }else if(type.equals("NYveggie"){
     pizza = new NYViggePizza();
  }else if(...){
   ...
  }
  return pizza;
 }
}

缺點:依賴的具體類太多,難以維護。缺乏彈性。

優化方法:可以把createPizza()抽象成一個工廠介面,再由三個不同的加盟商去實現三個不同的工廠類。具體的建立物件的工作,由子類來實現。

每個區域型別建立一個PizzaStore(NYPizzaStore、ChicagoPizzaStore、CaliforniaPizzaStore),由每個子類各自決定如何製作披薩。

每個子類都會覆蓋createPizza()方法,同時使用PizzaStore的orderPizza()方法。

如果加盟店為顧客提供紐約風味的的披薩,就使用NYStylePizzaStore。

採用工廠方法實現:

public abstract class PizzaStore{
  public Pizza orderPizza(String type){
  Pizza pizza = createPizza(type);
  pizza.prepare(); 佐料)
  pizza.bake(); //烘烤
  pizza.cut(); //切片
  pizza.box(); //裝盒
  return pizza;
  }
  abstract Pizza createPizza(String type);
}

public class NYStylePizzaStore extend PizzaStore{
  public Pizza createPizza(String type){
  Pizza pizza;
  if(type.equals("NYcheese")){
     pizza = new NYCheesePizza();
  }else if(type.equals("NYpepperoni")){
     pizza = new NYPepperoniPizza():
  }else if(type.equals("NYclam")){
     pizza = new NYClamPizza();
  }else if(type.equals("NYveggie"){
  pizza = new NYViggePizza();
  return pizza;
 }
}

public class ChicagoStylePizzaStore extend PizzaStore{...}

public class CaliforniaStylePizzaStore extend PizzaStore{...}

原本由一個物件負責所有具體類的例項化,現在通過PizzaStore改為由一群子類來負責例項化。

abstract Pizza createPizza(String type);工廠方法用來處理物件的建立,並將這樣的行為封裝在子類中。這樣,客戶程式中關於超類的程式碼就和子類物件建立程式碼解耦了。1)工廠方法必須是抽象的,所以依賴子類來處理物件的建立。2)工廠方法必須返回一個產品。3)工廠方法將客戶和實際建立具體產品程式碼分隔開。4)具體產品子類都繼承自同一個產品抽象類。

工廠方法模式:定義了一個建立物件的介面,但由子類決定要例項化的類是哪一個。工廠方法讓類把例項化推遲到子類。

工廠方法幫我們將產品的實現從產品的使用解耦,如果增加或改變產品的實現,Creator類並不會受影響。簡單工廠是把所有工作放在一個地方全部處理完。工廠方法建立的是一個框架,讓子類決定如何實現。

工廠方法模式的用意是定義一個建立產品物件的工廠介面,將實際建立工作推遲到子類中。

工廠方法抽象類角色:是工廠方法模式的核心,任何在模式中建立物件的工廠類必須實現這個介面。

面向物件原則:依賴倒置原則

當你在例項化一個物件時, 就是在依賴它的具體類。如果直接在PizzaOrder裡建立各種不同的具體披薩物件,那它的依賴所有具體的披薩類:

依賴倒置原則:要依賴抽象,不要依賴具體類。

與針對介面程式設計原則相比,這個原則更強調抽象,不能讓高層元件依賴低層元件。而且不管高層元件還是低層元件,兩者都應該依賴於抽象。高層元件:PizzaStore,低層元件:具體的披薩實現類如NYClamPizza。

思考倒置:需要實現一個披薩店,首先披薩店進行準備、烘烤、裝盒,披薩店需要能夠製作不同的口味的披薩:芝士披薩、素食披薩等;先從頂端開始,然後往下到具體類。不想讓披薩店理會這些具體的類,要不然披薩店將會全都依賴這些具體類。採用倒置的方法,從披薩Pizza開始,素食披薩、芝士披薩都是披薩,共享同一個Pizza介面。披薩店使用披薩抽象即可,不需要理會具體的披薩類。

所有的披薩類都依賴披薩抽象,披薩店也依賴披薩抽象。我們已經倒置了一個拉薩店依賴具體披薩類的設計。

使用工廠方法之後,PizzaStore和具體的Pizza實現類都依賴於Pizza抽象類。

第四篇:抽象工廠方法模式講解

第三次需求變更:要如何確保每家加盟店使用高質量的原料?

建造一個生產原料的工廠,並將原料運送到各家加盟店。但是,不同地方的加盟店,使用的原料是不一樣的。 所以需要為紐約和芝加哥兩個加盟店準備兩組不同的原料。每個原料家族都包含了一種麵糰、一種醬料、一種芝士,以及一種海鮮佐料的型別,每個區域實現了一個完整的原料家族。

需求分析:我們要建造一個工廠來生產原料;這個工廠將負責建立原料家族中的每一種原料。每個原料都有一個對應的方法建立該原料。

public interface PizzaIngredientFactory{
  public Dough createDough(); //麵糰
  public Sauce createSauce(); //醬料
  public Cheese createCheese();//芝士
  public Veggies[] createVeggies();//蔬菜
  public Pepperoni createPepperoni();//香腸
  public Clams createClam();//蛤蜊
}

處理方法:1)為每個區域建造一個工廠。建立一個繼承自PizzaIngredientFactory的子類來實現每一個建立方法。2)實現一組原料類供工廠使用。如ReggianoCheese、RedPeppers等。這些類可以在合適的區域間共享。

1)我們引入新型別的工廠,也就是所謂的抽象工廠,來建立披薩原料家族。

2)通過抽象工廠所提供的介面,可以建立產品的家族,利用這個介面,我們的程式碼將從實際工廠解耦,以便在不同上下文中實現各式各樣的工廠,製造出各種不同的產品。

3)因為程式碼從實際的產品中解耦了,所以我們可替換不同的工廠來取得不同的行為。

抽象工廠模式:提供一個介面,用於建立相關或依賴物件的家族,而不需要明確指定具體類。

1)抽象工廠允許客戶使用抽象的介面來建立一組相關的產品,而不需要知道實際產出的具體產品是什麼。這樣一來,客戶就從具體的產品中解耦。

2)抽象工廠的方法經常以工廠方法的方式實現。抽象工廠的任務是定義一個負責建立一組產品的介面。這個介面內的每個方法都負責建立一個具體產品,同時我們利用實現抽象工廠的子類來提供這些具體的做法。

每一個模式都是針對一定問題的解決方案。

抽象工廠模式與工廠方法模式的最大區別就在於,工廠方法模式針對的是一個產品等級結構;而抽象工廠模式則需要面對多個產品等級結構。

產品族,是指位於不同產品等級結構中,功能相關聯的產品組成的家族。如AMD的主機板、晶片組、CPU組成一個家族,Intel的主機板、晶片組、CPU組成一個家族。

而這兩個家族都來自於三個產品等級:主機板、晶片組、CPU。一個等級結構是由相同的結構的產品組成。

第五篇:案例實踐

簡單工廠方法

SimplePizzaFactoryDemo簡單披薩工廠案例:

當需要披薩時,就叫披薩工廠建立一個。

工廠方法模式(FactoryMethod)

FactoryMethodPizzaDemo工廠方法模式披薩工廠案例:

每個區域型別建立一個PizzaStore(NYPizzaStore、ChicagoPizzaStore、CaliforniaPizzaStore),每個子類各自決定如何製作披薩。

工廠方法用來處理物件的建立,並將這樣的行為封裝在子類中。這樣,客戶程式中關於超類的程式碼就和子類物件建立程式碼解耦了。

ExportFileDemo匯出報表檔案案例:

系統需要支援對資料庫中的資料進行匯出,

1)並且支援多種匯出格式,如:HTML、PDF等,

2)每種格式匯出的結構有所不同,如標準報表、財務報表等。

FactoryHumanDemo女媧造人案例:

女媧使用乾坤鼎造出不同膚色的人。

對造人過程進行分析,該過程涉及三個物件:女媧、乾坤鼎、三種不同膚色的人。

女媧可以用客戶來表示,乾坤鼎類似於一個工廠,負責製造生產產品(即人類),

三種不同膚色的人,都是同一個介面下的不同實現類, 對於乾坤鼎來說都是它生產出的產品。

抽象工廠模式(AbstractFactory)

AbstractPizzaIngredientDemo披薩原料工廠:

披薩原料有:麵糰、醬料、乳酪等。

不同地區風味的披薩原料是不同的,紐約風味的披薩使用的原料是:薄餅麵糰、加番茄醬、雷奇亞乾酪乳酪等。

芝加哥風味的披薩使用的原料是:厚餅麵糰、李子番茄醬、馬蘇裡拉乳酪等

ComputerEngineerDemo電腦工程師組裝電腦案例:

我們在組裝電腦的時候,通常需要選擇一系列的配件,比如CPU、主機板等。

在確定裝機方案時,需要整體考慮各個配件之間的相容性。

比如:CPU和主機板,Intel的CPU根本就插不到AMD的主機板中。CPU物件和主機板物件其實是有關係的,需要相互匹配的。

案例分析:對於裝機工程師,只需要知道CPU和主機板的介面,而需要不知道具體實現。

第六篇:總結

簡單工廠模式把核心放在一個具體類上。

工廠方法模式退化後可以變得很像簡單工廠模式。設想如果非常確定一個系統只需要一個具體工廠類,那麼不妨把抽象工廠類合併到具體工廠類中去。

抽象工廠與工廠方法都是負責建立物件,工廠方法利用的是繼承,抽象工廠是通過物件組合。

利用工廠方法建立物件,需要擴充套件一個類,並覆蓋它的的工廠方法。其實整個工廠方法模式,只不過就是通過子類來建立物件。用這種做法,客戶只需要知道他們所使用的抽象型別就可以了,而由子類來負責決定具體型別。

抽象工廠用來建立一個產品家族的抽象型別,這個型別的子類定義了產品被產生的方法。抽象工廠如果加入新產品就必須改變介面。其一般使用工廠方法來實現具體工廠。

工廠方法模式:

優點:

1)良好的封裝性,程式碼結構清晰。一個物件建立是有條件約束的,如一個呼叫者需要一個具體的產品物件,只要知道這個產品的類名就可以了,不用知道建立物件的過程, 降低模組間的耦合。

2)工廠方法模式的擴充套件性非常優秀。在增加產品類的情況下,只要適當地修改具體的工廠類或擴充套件一個工廠類,就可以完成“擁抱變化”。

3)遮蔽產品類。呼叫者只需要關心產品的介面,只要介面保持不變,系統中的上層模組就不需要發生變化。因為產品類的例項化工作是由工廠類負責的,生成具體哪一個產品是由工廠類決定的。

4)工廠方法模式是典型的解耦框架。高層模組值需要知道產品的抽象類,其他的實現類都不用關心。

使用場景:

1)工廠方法模式是new一個物件的替代品,所有需要生成物件的地方都可以使用,但需要慎重地考慮是否要增加一個工廠類進行管理增加程式碼的複雜度。

2)需要靈活的、可擴充套件的框架時,可以考慮採用工廠方法模式。

抽象工廠模式

抽象工廠模式的好處:一個工廠等級結構可以創建出分屬於不同產品等級結構的一個產品族中的所有物件。

抽象工廠的功能是為一系列相關物件或相互依賴的物件建立一個介面。

由於抽象工廠定義的一系列物件通常是相關或相互依賴的,這些產品物件就構成了一個產品族,也就是抽象工廠定義了一個產品族。

優點:

分離介面和實現;客戶端使用抽象工廠來建立需要的物件,而客戶端根本就不知道具體的實現是誰。

使切換產品族變得容易;

產品族內的約束為非公開狀態。

缺點:

不太容易擴充套件新的產品;如果需要給整個產品族新增一個新的產品,那麼就需要修改抽象工廠,這樣就會導致修改所有的工廠實現類。

一個應用系統是由多人開發的,匯出的功能是你實現的,但是使用者是其他人。

這時候你應該設計的足夠靈活並儘可能降低兩者之間的耦合度,

當你修改或增加一個新的功能時,使用者不需要修改任何地方。

假如你的設計不夠靈活,可能一個小的需求變更,便使得你的程式碼結構發生改變,並導致使用者都要修改他們的程式碼。