《設計模式》學習筆記2——簡單工廠模式
定義
簡單工廠模式並不屬於GoF(Gang of Four四人組)23中設計模式,有些地方的解釋說因為簡單工廠模式太簡單,所以23中設計模式就沒有單獨列出。
但是簡單工廠模式在實際的應用中卻很常用,因此在劉偉老師的《設計模式》一書中就還是列了出來。
簡單工廠模式引用書中的定義如下:
簡單工廠模式(Simple Factory Pattern):定義一個工廠類,它可以根據參數的不同返回不同類的實例,被創建的實例通常都具有共同的父類。因為在簡單工廠模式中用於創建實例的方法是靜態(static)方法,因此簡單工廠模式又被稱為靜態工廠方法(Static Factory Method)模式,它屬於類創建型模式
理解
對於這個模式,我覺得說靜態工廠模式比說簡單工廠模式更能讓人記住。靜態工廠模式,通過靜態兩個字就可以知道需要有一個靜態的工廠方法,然後隻要再理解一下何謂工廠,基本上就記住了這個模式。
那麼何謂工廠呢,眾所周知,生產產品的地方就是工廠。
對於工廠而言,需要有能正常運行的生產線,這個生產線可能是全自動化的,也可能是人工操作的,除此之外,還需要有被生產的產品。
根據上邊的思路,我們便可以轉換為代碼,首先寫出我們的產品類:
package patterntest.simplefactorypattern; /** * 產品類 * * @author tzx * */ public class MyProduct { /** * 產品名稱 */ private String productName; /** * 產品規格 */ private String productSize; public String getProductName() { return productName; } public void setProductName(String productName) { this.productName = productName; } public String getProductSize() { return productSize; } public void setProductSize(String productSize) { this.productSize = productSize; } @Override public String toString() { return "MyProduct [productName=" + productName + ", productSize=" + productSize + "]"; } }
有了這樣一個產品類,便可以創建該類的具體產品實例,學習java的必然知道最基本的創建對象實例的方式就是使用new關鍵字,比如new MyProduct()
。就像下邊的代碼:
package patterntest.simplefactorypattern; /** * 消費者 * * @author tzx * */ public class Consumer { public static void main(String[] args) { System.out.println("我需要一個產品,於是我愉快的製造了一個"); MyProduct product = new MyProduct(); System.out.println("我製造的產品是這樣的:" + product.getProductSize()); } }
這種方式其實相當於現實生活中某個人需要一個什麼東西,然後自己製造一個出來。
這種情況現實生活中必然是存在的,但是也必然有一定的限製,不可能每個人都能製造每種東西,所以還有很多東西個人製造不出來,或者說個人製造的成本太高,因此便有了專門製造某類東西的工廠,也就是我們的工廠類:
package patterntest.simplefactorypattern; /** * 簡單工廠 * * @author tzx * */ public class MySimpleFactory { /** * 外部獲取產品的工廠方法 */ public MyProduct getProduct() { return new MyProduct(); } }
那麼這個時候,我們需要用某個東西的人,簡稱為消費者,就不需要自己再製造這類東西,也不需要如何製造這類東西,可以更加方便的使用:
package patterntest.simplefactorypattern; /** * 消費者 * * @author tzx * */ public class Consumer { public static void main(String[] args) { System.out.println("我需要一個產品,於是我向工廠買了一個"); MyProduct product = new MySimpleFactory().getProduct(); System.out.println("我買的產品是這樣的:" + product.getProductSize()); } }
這樣一來,我們需要的東西隻需要調用工廠的購買方法,或者說工廠提供給外邊的獲取產品的方法,然後就能獲取到想要的產品了。
然而,有一個很不愉快的問題出現了,上邊的代碼中,產品確實不需要自己創建,不需要自己new了,但是工廠卻是被我們new出來的。
這樣問題就大了,相當於我僅僅需要某個產品,然後為了這個產品,我必須自己創建了一個工廠,而且每個需要這個產品的人都要創建一個該產品的工廠,這顯然是不合理也不可能的。
我們需要的僅僅是這樣而已:
MyProduct product = MySimpleFactory.getProduct();
那麼工廠方法也就需要改成靜態的,使我們隻要用類名就可以調用獲取產品的方法:
/** * 外部獲取產品的工廠方法 */ public static MyProduct getProduct() { return new MyProduct(); }
好了,現在對於某個產品,工廠就合理的提供了一個給外邊獲取產品的方法,而外邊的消費者也能通過一聲MySimpleFactory.getProduct()
的唿喚愉快的獲取到需要的產品了。
然而,如果後邊這個工廠擴大了,開始運營的產品不再單一了,例如牙膏,一開始隻是成年人用的牙膏,後邊又增加了兒童牙膏,那麼很顯然這時候我們工廠提供的獲取產品的無參方法就難以繼續擔當大任了。
因為隻通過一聲我要買牙膏
的唿喚,工廠根本無法知道消費者需要的是哪種,所以我們的工廠方法需要有參數,並且根據不同的參數給予不同的產品:
public static MyProduct getProduct(int type) { return new MyProduct(); }
現在,基本解決了上邊不知道消費者具體要什麼的問題,但是,工廠內部卻犯難了,因為根據原本產品製造說明說(產品類的定義),工廠能製造出來的產品隻有一種啊。
所以,原本的產品也需要升級,也就是本身的默認無參構造器不能滿足現有的需求了,我們可能需要增加有參數的構造方法,需要升級原本的產品製造說明書,並通過參數來確定具體的產品。
public MyProduct(int type) { if (0 == type) { // 提供成年人牙膏 } else if (1 == type) { // 提供兒童牙膏 } }
那麼以上的一些問題也暫時解決了,不管是需要兒童牙膏還是普通牙膏,工廠都能正確的提供,消費者也都能正確的獲取。
public static MyProduct getProduct(int type) { return new MyProduct(type); }
隻是,如果後邊工廠又擴大了,還要加入環保牙膏、特效美白牙膏呢?那麼我們就需要不斷的修改原本的產品構造方法,就如一本產品製造說明書要繼續加厚。
很顯然,這將導致那個有參數的構造方法越來越臃腫,如果每種產品的製造方法都異常複雜的話,我們的一本產品製造說明書也將可能達到需要人抬的地步。
所以,這樣不是辦法,我們需要把各自具體產品的製造說明書分開,也就是我們的產品類需要由一個變成多個:
/** * 普通牙膏 * * @author tzx * */ public class MyProduct1 { /** * 產品名稱 */ private String productName; /** * 產品規格 */ private String productSize; public MyProduct1() { addIngredient(); } /** * 添加必要成分 */ public void addIngredient() { } public String getProductName() { return productName; } public void setProductName(String productName) { this.productName = productName; } public String getProductSize() { return productSize; } public void setProductSize(String productSize) { this.productSize = productSize; } @Override public String toString() { return "MyProduct [productName=" + productName + ", productSize=" + productSize + "]"; } }
/** * 兒童牙膏 * * @author tzx * */ public class MyProduct2 { /** * 產品名稱 */ private String productName; /** * 產品規格 */ private String productSize; public MyProduct2() { addIngredient(); delead(); } /** * 去鉛 */ public void delead() { } /** * 添加必要成分 */ public void addIngredient() { } public String getProductName() { return productName; } public void setProductName(String productName) { this.productName = productName; } public String getProductSize() { return productSize; } public void setProductSize(String productSize) { this.productSize = productSize; } @Override public String toString() { return "MyProduct [productName=" + productName + ", productSize=" + productSize + "]"; } }
明眼人應該一眼就看出了上述代碼的問題,這兩個類幾乎一模一樣,唯獨不同的就是兒童牙膏增加了一個去鉛的方法。
很顯然,這樣的代碼是嚴重重複,不被看好的代碼,十足的浪費資源。所以我們需要把這些共同的東西給提取出來,形成一個父類,而這個父類不是具體的產品類,就需要聲明為抽象類,然後我們的產品類就應該是這樣了:
/** * 產品類父類 * * @author tzx * */ public abstract class MyProduct { /** * 產品名稱 */ protected String productName; /** * 產品規格 */ protected String productSize; /** * 添加必要成分 */ public void addIngredient() { } public String getProductName() { return productName; } public void setProductName(String productName) { this.productName = productName; } public String getProductSize() { return productSize; } public void setProductSize(String productSize) { this.productSize = productSize; } @Override public String toString() { return "MyProduct [productName=" + productName + ", productSize=" + productSize + "]"; } }
/** * 普通牙膏 * * @author tzx * */ public class MyProduct1 extends MyProduct { public MyProduct1() { addIngredient(); } }
/** * 兒童牙膏 * * @author tzx * */ public class MyProduct2 extends MyProduct { public MyProduct2() { addIngredient(); delead(); } /** * 去鉛 */ public void delead() { } }
那麼工廠類中具體產品的選擇,就可以改成下邊這樣:
/** * 外部獲取產品的工廠方法 */ public static MyProduct getProduct(int type) { MyProduct product = null; if (0 == type) { product = new MyProduct1(); } else if (1 == type) { product = new MyProduct2(); } return product; }
要點
好了,一個比較標準的簡單工廠模式就出來了,由上邊的分析可以知道簡單工廠模式的幾個要點:
1. 需要有一個生產產品的產品類(工廠)和靜態的輸出產品的方法,這個方法包含一些必要判斷邏輯;
2. 需要有一個抽象的產品父類,定義產品的公共屬性和方法;
3. 需要有具體的產品類;
4. 隻要提供正確的參數,就能通過靜態工廠方法獲取到具體的產品。
總結
通過上邊的一係列分析和實例,可以知道簡單工廠模式的一些優點,他可以使消費者不必關心具體產品的創建,隻需要一個參數就能得到需要的產品,實現了一定程度的鬆耦合。
同時,不同的產品不同的子類,也使得後續更容易拓展。
但是由於所有產品都通過工廠創建,不同產品獲取的邏輯判斷都在工廠方法中,因此具體的產品和工廠的耦合度就必然增大,同時在產品類的基礎上也額外的增加了工廠類。
所以,如果產品很多的話,這種簡單工廠模式便會變得臃腫而不易維護。
最後更新:2017-11-21 07:34:30