工廠模式
用工廠方法代替了new操作, 將選擇實(shí)現(xiàn)類
,創(chuàng)建對(duì)象
統(tǒng)一管理和控制.從而將調(diào)用者(Client)與實(shí)現(xiàn)類進(jìn)行解耦.實(shí)現(xiàn)了創(chuàng)建者與調(diào)用者分離
;
JDK中Calendar的getInstance方法; JDBC中Connection對(duì)象的獲取; MyBatis中SqlSessionFactory創(chuàng)建SqlSession; SpringIoC容器創(chuàng)建并管理Bean對(duì)象; 反射Class對(duì)象的newInstance; ….
靜態(tài)工廠模式
靜態(tài)工廠模式是工廠模式中最簡(jiǎn)單的一種,他可以用比較簡(jiǎn)單的方式隱藏創(chuàng)建對(duì)象的細(xì)節(jié),一般只需要告訴工廠類所需要的類型,工廠類就會(huì)返回需要的產(chǎn)品類,而客戶端看到的也只是產(chǎn)品的抽象對(duì)象(interface),因此無(wú)需關(guān)心到底是返回了哪個(gè)子類
我們以運(yùn)算符類為例, 解釋靜態(tài)工廠模式.
Operator接口
<code class="hljs" scala="">/** * 運(yùn)算符接口 * Created by jifang on 15/12/7. */public interface Operator<t>{ T getResult(T... args);}</t></code>實(shí)現(xiàn)類
<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="">/** * 靜態(tài)工廠(注: 只返回產(chǎn)品的抽象[即接口]) * 包含兩種實(shí)現(xiàn)策略 * 1. 根據(jù)傳入的operator名進(jìn)行實(shí)例化對(duì)象 * 2. 直接調(diào)用相應(yīng)的構(gòu)造實(shí)例的方法 * 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; } /* ** 第二種實(shí)現(xiàn)策略 ** */ 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>
優(yōu)點(diǎn)
隱藏了對(duì)象創(chuàng)建的細(xì)節(jié),將產(chǎn)品的實(shí)例化過(guò)程放到了工廠中實(shí)現(xiàn),工廠模式
。 客戶端基本不用關(guān)心使用的是哪個(gè)產(chǎn)品,只需要知道用工廠的那個(gè)方法(或傳入什么參數(shù))就行了. 方便添加新的產(chǎn)品子類,每次只需要修改工廠類傳遞的類型值就行了。 遵循了依賴倒轉(zhuǎn)
原則。缺點(diǎn)
適用于產(chǎn)品子類型差不多, 使用的方法名都相同的情況. 每添加一個(gè)產(chǎn)品子類,都必須在工廠類中添加一個(gè)判斷分支(或一個(gè)方法),這違背了OCP(開放-封閉原則)
。工廠方法模式
由于靜態(tài)工廠方法模式不滿足OCP
, 因此就出現(xiàn)了工廠方法模式
; 工廠方法模式和靜態(tài)工廠模式最大的不同在于:靜態(tài)工廠模式只有一個(gè)(對(duì)于一個(gè)項(xiàng)目/獨(dú)立模塊)只有一個(gè)工廠類, 而工廠方法模式則有一組實(shí)現(xiàn)了相同接口的工廠類.
工廠
<code class="hljs" scala="">/** * Created by jifang on 15/12/7. */public interface Factory<t>{ Operator<t>createOperator();}</t></t></code>工廠實(shí)現(xiàn)
<code class="hljs" php="">/** * 加法運(yùn)算符工廠 * 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="">/** * 乘法運(yùn)算符工廠 * 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
與上例相同.
此時(shí), 如果要在靜態(tài)工廠中新增加一個(gè)開根運(yùn)算類
, 要么需要在createOperator
方法中增加一種case
, 要么得增加一個(gè)createSqrtOper
方法, 都是需要修改原來(lái)的代碼的. 而在工廠方法中只需要再添加一個(gè)SqrtFactory
即可:
<code class="hljs" php="">/** * 開根運(yùn)算符 * 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>
優(yōu)點(diǎn)
基本與靜態(tài)工廠模式一致,多的一點(diǎn)優(yōu)點(diǎn)就是遵循了開放-封閉原則,使得模式的靈活性更強(qiáng)。
缺點(diǎn)
與靜態(tài)工廠模式差不多, 但是增加了類組織的復(fù)雜性;
小結(jié)
雖然根據(jù)理論原則, 需要使用工廠方法模式, 但實(shí)際上, 常用的還是靜態(tài)工廠模式.
抽象工廠模式
抽象工廠模式: 提供一個(gè)創(chuàng)建一系列相關(guān)或相互依賴對(duì)象的接口, 而無(wú)需指定他們具體的類.
抽象工廠模式與工廠方法模式的區(qū)別:
抽象工廠模式是工廠方法模式的升級(jí)版本,他用來(lái)創(chuàng)建一組相關(guān)或者相互依賴的對(duì)象。他與工廠方法模式的區(qū)別就在于,工廠方法模式針對(duì)的是一個(gè)產(chǎn)品等級(jí)結(jié)構(gòu)
;而抽象工廠模式則是針對(duì)的多個(gè)產(chǎn)品等級(jí)結(jié)構(gòu)
. 在編程中,通常一個(gè)產(chǎn)品結(jié)構(gòu),表現(xiàn)為一個(gè)接口或者抽象類,也就是說(shuō),工廠方法模式提供的所有產(chǎn)品都是衍生自同一個(gè)接口或抽象類,而抽象工廠模式所提供的產(chǎn)品則是衍生自不同的接口或抽象類(如下面的Engine, Tyre, Seat).在抽象工廠模式中,提出了產(chǎn)品族的概念:所謂的產(chǎn)品族,是指位于不同產(chǎn)品等級(jí)結(jié)構(gòu)中功能相關(guān)聯(lián)的產(chǎn)品組成的家族(如Engine, Tyre, Seat)。抽象工廠模式所提供的一系列產(chǎn)品就組成一個(gè)產(chǎn)品族;而工廠方法提供的一系列產(chǎn)品稱為一個(gè)等級(jí)結(jié)構(gòu).
示例:
現(xiàn)在我們要生產(chǎn)兩款車: 高檔(LuxuryCar)與低檔(LowCar), 他們分別配有高端引擎(LuxuryEngine), 高端座椅(LuxurySeat), 高端輪胎(LuxuryTyre)和低端引擎(LowEngine), 低端座椅(LowSeat), 低端輪胎(LowTyre), 下面我們用抽象工廠實(shí)現(xiàn)它:
LuxuryCarFactory
與LowCarFactory
分別代表一類產(chǎn)品族的兩款產(chǎn)品, 類似于數(shù)據(jù)庫(kù)產(chǎn)品族中有MySQL, Oracle, SqlServer
1. 產(chǎn)品
Engine<code axapta="" class="hljs">public interface Engine { void start(); void run();}class LowEngine implements Engine { @Override public void start() { System.out.println(啟動(dòng)慢 ...); } @Override public void run() { System.out.println(轉(zhuǎn)速慢 ...); }}class LuxuryEngine implements Engine { @Override public void start() { System.out.println(啟動(dòng)快 ...); } @Override public void run() { System.out.println(轉(zhuǎn)速快 ...); }}</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(旋轉(zhuǎn) - 不耐磨 ...); }}class LuxuryTyre implements Tyre { @Override public void revolve() { System.out.println(旋轉(zhuǎn) - 不磨損 ...); }}</code>
注意: 其中并沒(méi)有車類
2. 產(chǎn)品族Factory
Factory<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>優(yōu)點(diǎn)
封裝了產(chǎn)品的創(chuàng)建,使得不需要知道具體是哪種產(chǎn)品,只需要知道是哪個(gè)工廠就行了,
電腦資料
《工廠模式》(http://www.shangyepx.com)。 可以支持不同類型的產(chǎn)品,使得模式靈活性更強(qiáng)。 可以非常方便的使用一族中間的不同類型的產(chǎn)品。 缺點(diǎn)結(jié)構(gòu)太過(guò)臃腫,如果產(chǎn)品類型比較多,或者產(chǎn)品族類比較多,就會(huì)非常難于管理。 每次如果添加一組產(chǎn)品,那么所有的工廠類都必須添加一個(gè)方法,這樣違背了開放-封閉原則。所以一般適用于產(chǎn)品組合產(chǎn)品族變化不大的情況。
使用靜態(tài)工廠優(yōu)化抽象工廠
由于抽象工廠模式存在結(jié)構(gòu)臃腫以及改動(dòng)復(fù)雜的缺點(diǎn)(比如我們每次需要構(gòu)造Car, 都需要進(jìn)行CarFactory factory = new XxxCarFactory();
, 而一般一個(gè)項(xiàng)目中只會(huì)生產(chǎn)一種Car, 如果我們需要更改生產(chǎn)的車的類型, 那么客戶端的每一處調(diào)用都需要修改), 因此我們可以使用靜態(tài)工廠對(duì)其進(jìn)行改造, 我們使用CarCreator
來(lái)統(tǒng)一創(chuàng)建一個(gè)產(chǎn)品族不同產(chǎn)品, 這樣如果我們的工廠將來(lái)更改了產(chǎn)品路線, 改為生產(chǎn)高端車時(shí), 我們僅需改變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>
其實(shí)我們還可以通過(guò)反射, 將CarCreator
中的switch-case
去掉, 而且在實(shí)際開發(fā)中, 字符串的值我們還可以從配置文件中讀取, 這樣, 如果需要更改產(chǎn)品路線, 我們連程序代碼都懶得改了, 只需要修改配置文件就可以了.
<code class="hljs" java="">/** * Created by jifang on 15/12/7. */public class CarCreatorReflect { /** * 在實(shí)際開發(fā)中, 下面這些常量可以從配置文件中讀取 */ 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>
這樣, 客戶端調(diào)起來(lái)就清爽多了
<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>
小結(jié)
需要修改已有代碼
工廠方法模式用來(lái)生成同一等級(jí)結(jié)構(gòu)的固定產(chǎn)品, 支持增加任意產(chǎn)品;抽象工廠模式用來(lái)生成不同產(chǎn)品族
的全部產(chǎn)品, 對(duì)于增加新的產(chǎn)品無(wú)能為力;