4.Factory Pattern(工廠模式)

来源:http://www.cnblogs.com/lanshanxiao/archive/2017/12/07/7999847.html
-Advertisement-
Play Games

工廠模式(Factory Pattern)定義: 定義了一個創建對象的介面,但由子類決定要實例化的類是哪一個。工廠方法讓類把實例化推遲到子類。 針對實現編程,但是當我們每次使用new時候,不正是在針對實現編程嗎?是的,當使用“new”時,你的確實在實例化一個具體類,所以用的確實是實現,而不是介面。代 ...


工廠模式(Factory Pattern)定義:

  定義了一個創建對象的介面,但由子類決定要實例化的類是哪一個。工廠方法讓類把實例化推遲到子類。

  針對實現編程,但是當我們每次使用new時候,不正是在針對實現編程嗎?是的,當使用“new”時,你的確實在實例化一個具體類,所以用的確實是實現,而不是介面。代碼綁定這具體類會導致代碼更脆弱,更缺乏彈性。

  Duck duck                          =                           new GreenDuck();

  使用介面讓代碼具有彈性                                 但還是得建立具體類的實例

  但我們總是要用new來創建對象吧。是的,在技術上,new沒有錯,畢竟這是Java基礎部分。真正的犯人是我們的老朋友 “改變”,以及它是如何影響new的使用的。

看下麵的代碼:

 1 /*當有一群相關的具體類時,通常會寫出這樣的代碼*/
 2 Duck duck;
 3 
 4 if(picnic){
 5     duck = new GreenDuck();
 6 }else if(hunting){
 7     duck = new DecoyDuck();
 8 }else if(inBathTub){
 9     duck = new RubberDuck();
10 }...//還有更多的實現類,在運行程式時,要通過判斷,來確定創建哪個對象
View Code

  針對介面編程,可以隔離掉以後系統可能發生的一大堆改變。如果代碼是針對介面編程而寫,那麼通過多態,它可以與任何新類實現該介面。但是,當代碼使用大量的具體類時,等於是自找麻煩,因為一旦加入新的具體類,就必須改變代碼。也就是說,你的代碼並非 “對修改關閉”。想用新的具體類型來擴展代碼,必須重新打開它。所以,遇到問題時候,就應該回到OO設計原則去尋找線索。別忘了,我們的第一個原則用來處理改變,並幫助我們 “找出會變化的方面,把它們從不變的部分分離出來”

舉例:

  有一家比薩店,定製比薩的方法如下:

 1 Pizza orderPizza(String type){//這隻是類中一個方法,將想要定製的比薩的type類型傳入,就會製作出相應的比薩
 2     Pizza pizza;
 3     
 4     if(type.equals("cheese")){//根據比薩的type類型,實例化正確的具體類,然後將其賦值給pizza實例變數。
 5         pizza = new CheesePizza();//請註意,這裡的任何比薩都必須實現Pizza介面
 6     }else if(type.equals("greek"){
 7         pizza = new GreekPizza();
 8     }else if(type.equals("pepperoni"){
 9         pizza = new PepperoniPizza();
10     }
11     
12     
13     pizza.prepare();//一旦知道要製作什麼類型的比薩,那麼我們將要進行加工
14     pizza.bake();//這四個方法,分別是製作比薩的四個步驟
15     pizza.cut();
16     pizza.box();
17     
18     return pizza;
19 }
orderPizza

 正如上面所說,我們要製作新的比薩具體類或者刪除現有的比薩具體類,都需要打開orderPizza()方法所在的類,來添加或刪除裡面的實例化。

我們要做的是,找出會變化的方面,把它們從不變的部分分離出來,現在我們將判斷製作比薩類型部分從orderPizza()方法中分離出來:

 1 Pizza orderPizza(String type){//這隻是類中一個方法,將想要定製的比薩的type類型傳入,就會製作出相應的比薩
 2     Pizza pizza;
 3     
 4     //原先實例化比薩的部分被分離出去
 5     
 6     pizza.prepare();//一旦知道要製作什麼類型的比薩,那麼我們將要進行加工
 7     pizza.bake();//這四個方法,分別是製作比薩的四個步驟
 8     pizza.cut();
 9     pizza.box();
10     
11     return pizza;
12 }
orderPizza
 1 public class SimplePizzaFactory{
 2     public Pizza createPizza(String type){
 3         Pizza pizza = null;
 4         
 5         if(type.equals("cheese")){//根據比薩的type類型,實例化正確的具體類,然後將其賦值給pizza實例變數。
 6             pizza = new CheesePizza();//請註意,這裡的任何比薩都必須實現Pizza介面
 7         }else if(type.equals("pepperoni"){//刪除了一些比薩,又增加了一些比薩
 8             pizza = new PepperoniPizza();
 9         }else if(type.equals("clam"){
10             pizza = new ClamPizza();
11         }else if(type.equals("veggie"){
12             pizza = new VeggiePizza();
13         }
14         
15         return pizza;
16     }
17 }
SimplePizzaFactory

  從上面代碼可以看出,我們將orderPizza()方法中實例化比薩的部分分離出來,單獨放入了SimplePizzaFactory類中。

  那麼,關於new的問題還是沒有解決,我們還是針對實現編程,只是把針對實現編程的部分搬到了另一個對象中。問題還是存在,沒錯,確實存在。但是我們已經做到最小化改變代碼(以我們現在所學的知識,還不足以脫離new來實例化對象,所以肯定要針對實現編程。若不能避免,那就做到最好!),若以後要修改創建比薩實例部分,就只要打開SimplePizzaFactory類就可以修改。

  現在我們將PizzaStore(比薩店)類實現:

 1 public class PizzaStore{
 2     SimplePizzaFactory factory;//比薩工廠對象
 3     
 4     public PizzaStore(SimplePizzaFactory factory){//在構造器中,需要一個工廠對象作為參數
 5         this.factory = factory;
 6     }
 7     
 8     public Pizza orderPizza(String type){
 9         Pizza pizza;
10         
11         pizza = factory.createPizza(type);//將實例化比薩對象的責任交給工廠對象
12         
13         pizza.prepare();//一旦知道要製作什麼類型的比薩,那麼我們將要進行加工
14         pizza.bake();//這四個方法,分別是製作比薩的四個步驟
15         pizza.cut();
16         pizza.box();
17         
18         return pizza;
19     }
20 }
PizzaStore

上面的就是簡單工廠模式,這隻是小本生意的開始。我們只開了一家比薩店,現在我們的比薩店出名了,要開跨地區連鎖店,規定每個地區開一家。我們做的比薩要符合當地人的口味。於是各個地區的比薩口味又不一樣。

  不多說先把Pizza抽象類實現了:

 1 import java.util.ArrayList;
 2 
 3 public abstract class Pizza{
 4     String name;//比薩名字
 5     String dough;//麵團
 6     String sauce;//
 7     ArrayList toppings = new ArrayList();//配料列表
 8     
 9     void prepare(){//準備做比薩方法
10         System.out.println("Preparing " + name);
11         System.out.println("Tossing dough...");
12         System.out.println("Adding sauce...");
13         System.out.println("Adding toppings: ");
14         
15         for(int i = 0; i < toppings.size(); i++){
16             System.out.println("   " + toppings.get(i));
17         }
18     }
19     
20     void bake(){//烘焙方法
21         System.out.println("Bake for 25 minutes at 350");
22     }
23     
24     void cut(){//切塊方法
25         System.out.println("Cutting the pizza into diagonal slices");
26     }
27     
28     void box(){//裝盒方法
29         System.out.println("Place pizza in official PizzaStore box");
30     }
31     
32     public String getName(){//獲取名稱方法
33         return name;
34     }
35 }
Pizza

  然後根據各地區做出不同的Pizza:

1 public class NYStyleCheesePizza extends Pizza{//NewYork比薩
2     public NYStyleCheesePizza(){
3         name = "NY Style Sauce and Cheese Pizza";
4         dough = "Thin Crust Dough";
5         sauce = "Marinara Sauce";
6         
7         toppings.add("Grated Reggiano Cheese");
8     }
9 }
NYStyleCheesePizza
 1 public class ChicagoStyleCheesePizza extends Pizza{//Chicago比薩
 2     public ChicagoStyleCheesePizza(){
 3         name = "Chicago Style Deep Dish Cheese Pizza";
 4         dough = "Extra Thick Crust Dough";
 5         sauce = "Plum Tomato Sauce";
 6         
 7         toppings.add("Shredded Mozzarella Cheese");
 8     }
 9     
10     void cut(){
11         System.out.println("Cutting the pizza into square slices");
12     }
13 }
ChicagoStyleCheesePizza

  實現PizzaStore抽象類:

 1 public abstract class PizzaStore{//所有地區的PizzaStore都要繼承該抽象類
 2     public Pizza orderPizza(String type){//定製Pizza方法
 3         Pizza pizza;
 4         
 5         pizza = createPizza(type);//創建Pizza實例
 6         
 7         pizza.prepare();
 8         pizza.bake();
 9         pizza.cut();
10         pizza.box();
11         
12         return pizza;
13     }
14     
15     abstract Pizza createPizza(String type);//創建Pizza實例的方法由每個地區的PizzaStore子類來實現
16 }
PizzaStore

  每個地區開一個PizzaStore:

1 public class NYPizzaStore extends PizzaStore{//NewYork店
2     Pizza createPizza(String item){
3         if(item.equals("cheese")){//可以製作出cheese口味的Pizza
4             return new NYStyleCheesePizza();
5         }else return null;//也可以製作出其他口味的Pizza,我就不實現了。
6     }
7 }
NYPizzaStore
1 public class ChicagoPizzaStore extends PizzaStore{//Chicago店
2     Pizza createPizza(String item){
3         if(item.equals("cheese")){//可以製作出Cheese口味的Pizza
4             return new ChicagoStyleCheesePizza();
5         }else return null;//也可以製作出其他口味的Pizza,我就不實現了。
6     }
7 }
ChicagoPizzaStore

  測試類:

 1 public class PizzaTestDrive{
 2     
 3     public static void main(String args[]){
 4         //先開店
 5         PizzaStore nyStore = new NYPizzaStore();
 6         PizzaStore chicagoStore = new ChicagoPizzaStore();
 7         
 8         //NYPizzaStore店製作出cheese口味的Pizza
 9         Pizza pizza = nyStore.orderPizza("cheese");
10         System.out.println("Ethan ordered a " + pizza.getName() + "\n");
11         //ChicagoPizzaStore店製作出cheese口味的Pizza
12         pizza = chicagoStore.orderPizza("cheese");
13         System.out.println("Joel ordered a " + pizza.getName() + "\n");
14     }
15 }
PizzaTestDrive

編譯運行結果:

   上面的就是工廠模式。

總結一下,有兩個抽象類:

1.Pizza抽象類:這個抽象類是產品抽象類,換句話說就是工廠要製作出的產品。所有的Pizza子類都要繼承自該Pizza抽象類。

2.PizzaStore抽象類:這個抽象類是工廠抽象類,這個抽象類中有一個createPizza()抽象方法,這個方法就是為了讓子類去實現自己的產品類。這樣一來,子類可以根據自己的情況,隨心所欲的生產符合自己口味的產品。

再細細地品味一下工廠模式的定義:

  工廠模式定義了一個創建對象的介面(工廠抽象類),但由工廠子類決定要實例化哪一個類。工廠方法讓類把實例化推遲到了工廠子類。

由工廠子類決定要實例化哪一個類,不要理解錯誤。並不是指模式允許子類本身在運行時做決定,而是指在編寫工廠抽象類時,不需要知道實際創建的產品是哪一個。選擇使用了哪個子類,自然就決定了實際創建的產品是什麼。

這裡溫馨提醒一下:在設計模式中,所謂的 “實現一個介面” 並 “不一定” 表示 “寫一個介面,並利用implements關鍵字來實現這個介面”。“實現一個介面” 泛指 “實現某個超類型(可以是類或介面)的某個方法” 。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 推薦方法一: <meta name="referrer" content="never"> 就這麼一句話,把它放到<head>頭裡就可以了。記得要把其他影響圖片的東西給刪了,不然會衝突。 我自己本人的,因為我希望點擊一個鏈接時跳轉到微信圖文,然後在該頁面點擊返回圖標從微信圖文返回之前的APP頁面,我 ...
  • 你遇到過性能很差的網頁嗎? 這種網頁響應非常緩慢,占用大量的CPU和記憶體,瀏覽起來常常有卡頓,頁面的動畫效果也不流暢。 你會有什麼反應?我猜想,大多數用戶會關閉這個頁面,改為訪問其他網站。作為一個開發者,肯定不願意看到這種情況,那麼怎樣才能提高性能呢? 本文將詳細介紹性能問題的出現原因,以及解決方法 ...
  • 第一種是比較常規的方法 思路: 1.構建一個新的數組存放結果 2.for迴圈中每次從原數組中取出一個元素,用這個元素迴圈與結果數組對比 3.若結果數組中沒有該元素,則存到結果數組中 代碼如下: Array.prototype.unique1 = function(){ var res = [this ...
  • 1、const聲明一個只讀的常量。一旦聲明,常量的值就不能改變。 2、const的作用域與let命令相同:只在聲明所在的塊級作用域內有效。 3、const命令聲明的常量也是不提升,只能在聲明的位置後面使用。 4、ES6中:var命令和function命令聲明的全局變數,依舊是頂層對象的屬性;另一方面 ...
  • 阿子原創,轉載請註明出處。 在使用jQuery Mobile Slider時,發現在頁面上下拖動時,很容易不小心觸發Slider的點擊事件,從而造成誤操作。為此需要禁用Slider的點擊事件。 官方API並沒有提供禁用點擊事件的事件的方法,因此要實現這一功能只能另闢蹊徑。 留意到點擊Slider時都 ...
  • 1、實現塊作用域 2、不存在變數提升。 ES6 明確規定,如果區塊中存在let和const命令,這個區塊對這些命令聲明的變數,從一開始就形成了封閉作用域。凡是在聲明之前就使用這些變數,就會報錯。 ...
  • 網頁性能優化 頁面優化 頁面優化可以提升頁面的訪問速度從而提高用戶體驗,優化的頁面可以更好的提升 SEO 的效果同時也可以提高代碼的可讀性和維護性。 網頁的生成過程,大致可以分成五步。 1. HTML代碼轉化成DOM 2. CSS代碼轉化成CSSOM(CSS Object Model) 3. 結合D ...
  • 超級偶數(SuperEven)是指每一位都是偶數的正整數,例如: 0,2,4,6,8,20,22,24,26,28,40,...,88,200,202,... 要求寫一個函數,輸入項數n,返回數列第n項的值。 說實話,這個題目整整花了我三天時間去思考(數學比較弱,大神見笑)#手動捂臉#。 其實到最後 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...