工廠模式 標簽: Java與設計模式
工廠模式
用工廠方法代替了new操作, 將選擇實現類,創建對象統一管理和控制.從而將調用者(Client)與實現類進行解耦.實現了創建者與調用者分離;
使用場景JDK中Calendar的getInstance方法; JDBC中Connection對象的獲取; MyBatis中SqlSessionFactory創建SqlSession; SpringIoC容器創建并管理Bean對象; 反射Class對象的newInstance; ….靜態工廠模式
靜態工廠模式是工廠模式中最簡單的一種,他可以用比較簡單的方式隱藏創建對象的細節,一般只需要告訴工廠類所需要的類型,工廠類就會返回需要的產品類,而客戶端看到的也只是產品的抽象對象(interface),因此無需關心到底是返回了哪個子類
我們以運算符類為例, 解釋靜態工廠模式.
Operator接口
<code class="hljs" scala="">/** * 運算符接口 * Created by jifang on 15/12/7. */public interface Operator<t>{ T getResult(T... args);}</t></code>實現類<code axapta="" class="hljs">public class AddOperator implements Operator<integer>{ @Override public Integer getResult(Integer... args) { int result = 0; for (int arg : args) { result += arg; } return result; }}</integer></code><code axapta="" class="hljs">public class MultiOperator implements Operator<integer>{ @Override public Integer getResult(Integer... args) { int result = 1; for (int arg : args) { result *= arg; } return result; }}</integer></code>工廠<code class="hljs" java="">/** * 靜態工廠(注: 只返回產品的抽象[即接口]) * 包含兩種實現策略 * 1. 根據傳入的operator名進行實例化對象 * 2. 直接調用相應的構造實例的方法 * Created by jifang on 15/12/7. */public class OperatorFactory { public static Operator<integer>createOperator(String operName) { Operator<integer>operator; switch (operName) { case +: perator = new AddOperator(); break; case *: perator = new MultiOperator(); break; default: throw new RuntimeException(Wrong Operator Name: + operName); } return operator; } /* ** 第二種實現策略 ** */ public static Operator<integer>createAddOper() { return new AddOperator(); } public static Operator<integer>createMultiOper() { return new MultiOperator(); }}</integer></integer></integer></integer></code>Client<code class="hljs" cs="">public class Client { @Test public void testAdd() { Operator<integer>operator = OperatorFactory.createOperator(+); System.out.println(operator.getResult(1, 2, 3, 4, 6)); } @Test public void testMultiplication() { Operator<integer>operator = OperatorFactory.createOperator(*); System.out.println(operator.getResult(1, 2, 3, 4, 6)); } @Test public void testAddName(){ Operator<integer>operator = OperatorFactory.createAddOper(); System.out.println(operator.getResult(1, 2, 3, 4, 6)); } @Test public void testMultiplicationName() { Operator<integer>operator = OperatorFactory.createMultiOper(); System.out.println(operator.getResult(1, 2, 3, 4, 6)); }}</integer></integer></integer></integer></code>優點
隱藏了對象創建的細節,將產品的實例化過程放到了工廠中實現,工廠模式。 客戶端基本不用關心使用的是哪個產品,只需要知道用工廠的那個方法(或傳入什么參數)就行了. 方便添加新的產品子類,每次只需要修改工廠類傳遞的類型值就行了。 遵循了依賴倒轉原則。缺點
適用于產品子類型差不多, 使用的方法名都相同的情況. 每添加一個產品子類,都必須在工廠類中添加一個判斷分支(或一個方法),這違背了OCP(開放-封閉原則)。工廠方法模式由于靜態工廠方法模式不滿足OCP, 因此就出現了工廠方法模式; 工廠方法模式和靜態工廠模式最大的不同在于:靜態工廠模式只有一個(對于一個項目/獨立模塊)只有一個工廠類, 而工廠方法模式則有一組實現了相同接口的工廠類.
工廠<code class="hljs" scala="">/** * Created by jifang on 15/12/7. */public interface Factory<t>{ Operator<t>createOperator();}</t></t></code>工廠實現<code class="hljs" php="">/** * 加法運算符工廠 * Created by jifang on 15/12/7. */public class AddFactory implements Factory<integer>{ @Override public Operator<integer>createOperator() { return new AddOperator(); }}</integer></integer></code><code class="hljs" php="">/** * 乘法運算符工廠 * Created by jifang on 15/12/7. */public class MultiFactory implements Factory<integer>{ @Override public Operator<integer>createOperator() { return new MultiOperator(); }}</integer></integer></code>
Operator,AddOperator與MultiOperator與上例相同.
此時, 如果要在靜態工廠中新增加一個開根運算類, 要么需要在createOperator方法中增加一種case, 要么得增加一個createSqrtOper方法, 都是需要修改原來的代碼的. 而在工廠方法中只需要再添加一個SqrtFactory即可:
<code class="hljs" php="">/** * 開根運算符 * Created by jifang on 15/12/7. */public class SqrtOperator implements Operator<double>{ @Override public Double getResult(Double... args) { if (args != null && args.length >= 1) { return Math.sqrt(args[0]); } else { throw new RuntimeException(Params Number Error + args.length); } }}</double></code><code class="hljs" php="">/** * 開根工廠 * Created by jifang on 15/12/7. */public class SqrtFactory implements Factory<double>{ @Override public Operator<double>createOperator() { return new SqrtOperator(); }}</double></double></code>
優點
基本與靜態工廠模式一致,多的一點優點就是遵循了開放-封閉原則,使得模式的靈活性更強。
缺點
與靜態工廠模式差不多, 但是增加了類組織的復雜性;
小結
雖然根據理論原則, 需要使用工廠方法模式, 但實際上, 常用的還是靜態工廠模式.
抽象工廠模式抽象工廠模式: 提供一個創建一系列相關或相互依賴對象的接口, 而無需指定他們具體的類.
抽象工廠模式與工廠方法模式的區別:
抽象工廠模式是工廠方法模式的升級版本,他用來創建一組相關或者相互依賴的對象。他與工廠方法模式的區別就在于,工廠方法模式針對的是一個產品等級結構;而抽象工廠模式則是針對的多個產品等級結構. 在編程中,通常一個產品結構,表現為一個接口或者抽象類,也就是說,工廠方法模式提供的所有產品都是衍生自同一個接口或抽象類,而抽象工廠模式所提供的產品則是衍生自不同的接口或抽象類(如下面的Engine, Tyre, Seat).在抽象工廠模式中,提出了產品族的概念:所謂的產品族,是指位于不同產品等級結構中功能相關聯的產品組成的家族(如Engine, Tyre, Seat)。抽象工廠模式所提供的一系列產品就組成一個產品族;而工廠方法提供的一系列產品稱為一個等級結構.
示例:
現在我們要生產兩款車: 高檔(LuxuryCar)與低檔(LowCar), 他們分別配有高端引擎(LuxuryEngine), 高端座椅(LuxurySeat), 高端輪胎(LuxuryTyre)和低端引擎(LowEngine), 低端座椅(LowSeat), 低端輪胎(LowTyre), 下面我們用抽象工廠實現它:
LuxuryCarFactory與LowCarFactory分別代表一類產品族的兩款產品, 類似于數據庫產品族中有MySQL, Oracle, SqlServer1. 產品Engine<code axapta="" class="hljs">public interface Engine { void start(); void run();}class LowEngine implements Engine { @Override public void start() { System.out.println(啟動慢 ...); } @Override public void run() { System.out.println(轉速慢 ...); }}class LuxuryEngine implements Engine { @Override public void start() { System.out.println(啟動快 ...); } @Override public void run() { System.out.println(轉速快 ...); }}</code>Seat<code axapta="" class="hljs">public interface Seat { void massage();}class LowSeat implements Seat { @Override public void massage() { System.out.println(不能按摩 ...); }}class LuxurySeat implements Seat { @Override public void massage() { System.out.println(可提供按摩 ...); }}</code>Tyre<code axapta="" class="hljs">public interface Tyre { void revolve();}class LowTyre implements Tyre { @Override public void revolve() { System.out.println(旋轉 - 不耐磨 ...); }}class LuxuryTyre implements Tyre { @Override public void revolve() { System.out.println(旋轉 - 不磨損 ...); }}</code>
注意: 其中并沒有車類
2. 產品族FactoryFactory<code class="hljs" java="">/** * Created by jifang on 15/12/7. */public interface CarFactory { Engine createEngine(); Seat createSeat(); Tyre createTyre();}</code>低端車<code class="hljs" java="">public class LowCarFactory implements CarFactory { @Override public Engine createEngine() { return new LowEngine(); } @Override public Seat createSeat() { return new LowSeat(); } @Override public Tyre createTyre() { return new LowTyre(); }}</code>高端車<code class="hljs" java="">public class LuxuryCarFactory implements CarFactory { @Override public Engine createEngine() { return new LuxuryEngine(); } @Override public Seat createSeat() { return new LuxurySeat(); } @Override public Tyre createTyre() { return new LuxuryTyre(); }}</code>3. Client<code class="hljs" java="">/** * Created by jifang on 15/12/7. */public class Client { @Test public void testLow(){ CarFactory factory = new LowCarFactory(); Engine engine = factory.createEngine(); engine.start(); engine.run(); Seat seat = factory.createSeat(); seat.massage(); Tyre tyre = factory.createTyre(); tyre.revolve(); } @Test public void testLuxury(){ CarFactory factory = new LuxuryCarFactory(); Engine engine = factory.createEngine(); engine.start(); engine.run(); Seat seat = factory.createSeat(); seat.massage(); Tyre tyre = factory.createTyre(); tyre.revolve(); }}</code>優點封裝了產品的創建,使得不需要知道具體是哪種產品,只需要知道是哪個工廠就行了,電腦資料《工廠模式》(https://www.unjs.com)。 可以支持不同類型的產品,使得模式靈活性更強。 可以非常方便的使用一族中間的不同類型的產品。 缺點
結構太過臃腫,如果產品類型比較多,或者產品族類比較多,就會非常難于管理。 每次如果添加一組產品,那么所有的工廠類都必須添加一個方法,這樣違背了開放-封閉原則。所以一般適用于產品組合產品族變化不大的情況。使用靜態工廠優化抽象工廠
由于抽象工廠模式存在結構臃腫以及改動復雜的缺點(比如我們每次需要構造Car, 都需要進行CarFactory factory = new XxxCarFactory();, 而一般一個項目中只會生產一種Car, 如果我們需要更改生產的車的類型, 那么客戶端的每一處調用都需要修改), 因此我們可以使用靜態工廠對其進行改造, 我們使用CarCreator來統一創建一個產品族不同產品, 這樣如果我們的工廠將來更改了產品路線, 改為生產高端車時, 我們僅需改變CAR_TYEP的值就可以了:
<code class="hljs" java="">/** * Created by jifang on 15/12/7. */public class CarCreator { private static final String CAR_TYPE = low; private static final String CAR_TYPE_LOW = low; private static final String CAR_TYPE_LUXURY = luxury; public static Engine createEngine() { Engine engine = null; switch (CAR_TYPE) { case CAR_TYPE_LOW: engine = new LowEngine(); break; case CAR_TYPE_LUXURY: engine = new LuxuryEngine(); break; } return engine; } public static Seat createSeat() { Seat seat = null; switch (CAR_TYPE) { case CAR_TYPE_LOW: seat = new LowSeat(); break; case CAR_TYPE_LUXURY: seat = new LuxurySeat(); break; } return seat; } public static Tyre createTyre() { Tyre tyre = null; switch (CAR_TYPE) { case CAR_TYPE_LOW: tyre = new LowTyre(); break; case CAR_TYPE_LUXURY: tyre = new LuxuryTyre(); break; } return tyre; }}</code>其實我們還可以通過反射, 將CarCreator中的switch-case去掉, 而且在實際開發中, 字符串的值我們還可以從配置文件中讀取, 這樣, 如果需要更改產品路線, 我們連程序代碼都懶得改了, 只需要修改配置文件就可以了.
<code class="hljs" java="">/** * Created by jifang on 15/12/7. */public class CarCreatorReflect { /** * 在實際開發中, 下面這些常量可以從配置文件中讀取 */ private static final String PACKAGE = com.feiqing.abstractfactory; private static final String ENGINE = LuxuryEngine; private static final String TYRE = LuxuryTyre; private static final String SEAT = LuxurySeat; public static Engine createEngine() { String className = PACKAGE + . + ENGINE; try { return (Engine) Class.forName(className).newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } return null; } public static Seat createSeat() { String className = PACKAGE + . + SEAT; try { return (Seat) Class.forName(className).newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } return null; } public static Tyre createTyre() { String className = PACKAGE + . + TYRE; try { return (Tyre) Class.forName(className).newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } return null; }}</code>這樣, 客戶端調起來就清爽多了
<code class="hljs" java="">/** * Created by jifang on 15/12/7. */public class StaticClient { @Test public void testLow() { Engine engine = CarCreator.createEngine(); engine.run(); engine.start(); Seat seat = CarCreator.createSeat(); seat.massage(); Tyre tyre = CarCreator.createTyre(); tyre.revolve(); } @Test public void testLuxury() { Engine engine = CarCreatorReflect.createEngine(); engine.run(); engine.start(); Seat seat = CarCreatorReflect.createSeat(); seat.massage(); Tyre tyre = CarCreatorReflect.createTyre(); tyre.revolve(); }}</code>小結分類說明靜態工廠模式用來生成同一等級結構中的任意產品, 對于增加新的產品,需要修改已有代碼工廠方法模式用來生成同一等級結構的固定產品, 支持增加任意產品;抽象工廠模式用來生成不同產品族的全部產品, 對于增加新的產品無能為力;