又是新的一天,今天是19大閉幕式,呵呵我的股票長了點(diǎn),唉總算是收回了點(diǎn)成本,我是實(shí)踐中深刻體會(huì)到了這句話的內(nèi)涵,股市有風(fēng)險(xiǎn),入市需謹(jǐn)慎;還是少玩的好;今天我們來玩玩工廠方法模式.
先看下定義:
工廠方法模式定義:Define an interface for creating an object,but let subclasses decide which class to
instantiate.Factory Method lets a class defer instantiation to subclasses.(定義一個(gè)用于創(chuàng)建對(duì)象的
接口,讓子類決定實(shí)例化哪一個(gè)類。工廠方法使一個(gè)類的實(shí)例化延遲到其子類。),
工廠方法模式是一個(gè)應(yīng)用最廣泛的設(shè)計(jì)模式,也是一個(gè)高度解耦的設(shè)計(jì)模式:咱們今天從代碼中展開,相信任何一個(gè)有經(jīng)驗(yàn)的程序員一看就了然。
今天同事準(zhǔn)備買電腦讓我?guī)退扑]個(gè)電腦,咱們就以電腦為例吧:
public interface Computer {
/**
* 電腦型號(hào)
*
* @return
*/
public String getModelNumber();
/**
* 電腦出廠價(jià)
*
* @return
*/
public String getComputerPrice();
/**
* 電腦的基本配置
*
* @return
*/
public String getComputerDesc();
}
public class InspirationComputer implements Computer {
@Override
public String getModelNumber() {
return "靈越系列Ins15E-3725";
}
@Override
public String getComputerPrice() {
return "3333元";
}
@Override
public String getComputerDesc() {
return "適用場景 家庭影音 女性定位 輕薄便攜 學(xué)生 商務(wù)辦公 高清游戲 家庭娛樂";
}
}
public class VostroComputer implements Computer {
@Override
public String getModelNumber() {
// TODO Auto-generated method stub
return "Vostro 5459-1528";
}
@Override
public String getComputerPrice() {
// TODO Auto-generated method stub
return "¥ 4499.00";
}
@Override
public String getComputerDesc() {
// TODO Auto-generated method stub
return "適用場景 商務(wù)辦公 家庭娛樂";
}}
戴爾工廠里生產(chǎn)電腦;那么電腦有型號(hào)有價(jià)格,和基本配置定位;這是產(chǎn)品的共有特性;我們用一個(gè)接口來統(tǒng)一定義這些方法;注意面向接口編程一個(gè)好處就是便于擴(kuò)展;有了電腦就應(yīng)該有工廠去生產(chǎn):
/**
* 抽象工廠上層
*
*/
public abstract class AbsrtactComputerFactory {
// 限制傳入類型只能是Computer的子類
public abstract <T extends Computer> T createComputer(Class<T> c);
}
/**
* 具體的工廠方法實(shí)現(xiàn),目的就是生產(chǎn)電腦
*
* @author Administrator
*
*/
public class ComputerFactory extends AbsrtactComputerFactory {
@Override
public <T extends Computer> T createComputer(Class<T> c) {
Computer computer = null;
try {
computer = (T) Class.forName(c.getName()).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return (T) computer;
}}
好了現(xiàn)在我們有了工廠了,可以開工了:
public static void main(String[] args) {
// TODO Auto-generated method stub
AbsrtactComputerFactory compFactory = new ComputerFactory();
Computer inspirationComputer = compFactory.createComputer(InspirationComputer.class);
System.out.println(inspirationComputer.getComputerDesc() + "\n" + inspirationComputer.getModelNumber() + "\n"
+ inspirationComputer.getComputerPrice());
Computer vostroComputer = compFactory.createComputer(VostroComputer.class);
System.out.println(vostroComputer.getComputerDesc() + "\n" + vostroComputer.getModelNumber() + "\n"
+ vostroComputer.getComputerPrice());
}

由圖可見,我們構(gòu)造了一個(gè)工廠生產(chǎn)了兩臺(tái)不同型號(hào)的電腦,簡單吧,這就是工廠方法模式的通用用法。
列舉下他的優(yōu)點(diǎn)吧:
1.良好的封裝性,代碼結(jié)構(gòu)清晰;
一個(gè)對(duì)象創(chuàng)建是有條件約束的,如一個(gè)調(diào)用者需要一個(gè)具體的產(chǎn)品對(duì)象,只要知道這個(gè)產(chǎn)品的類名(或約束字符串)就可以了,不用知道創(chuàng)建對(duì)象的艱辛過程,降低模塊間的耦合;對(duì)于調(diào)用者來說我們只需要知道生產(chǎn)什么型號(hào)的電腦,知道類名就可以,不需要關(guān)注這個(gè)類怎么創(chuàng)建的.
2.擴(kuò)展性非常優(yōu)秀;
就代碼而言,如果我們需要生產(chǎn)一個(gè)X型號(hào)的電腦,具有特殊特性不具備公共特性怎么辦?我們只需要增加一個(gè)X型即可,并且再此進(jìn)行特性擴(kuò)展,不需要修改工廠生產(chǎn)的上層封裝;
如果盤子大了怎么辦?遇到業(yè)務(wù)代碼很多,我們是不是可以考慮開辟不同的工廠專司其職呢,比如我們想戴爾靈越系列專職由一個(gè)工廠生產(chǎn),Vostro系列專由一個(gè)工廠生產(chǎn),結(jié)構(gòu)更清晰,我們完全可以創(chuàng)建不同的工廠去做,這也是面向接口編程的好處,但是也有弊端,因?yàn)榫S護(hù)成本就高了,產(chǎn)品和工廠是一一對(duì)應(yīng)的關(guān)系,有幾個(gè)產(chǎn)品就要?jiǎng)?chuàng)建幾個(gè)工廠.
3.屏蔽產(chǎn)品類
產(chǎn)品類的實(shí)現(xiàn)如何變化,調(diào)用者都不需要關(guān)心,它只需要關(guān)心產(chǎn)品的接口,只要接口保持不變,系統(tǒng)中的上層模塊就不會(huì)發(fā)生變化,也就是從上到下的過程,
以上是工廠方法模式的標(biāo)準(zhǔn)寫法,下面我們看看工廠方法模式的幾個(gè)變種使用:
1.簡單工廠模式:
每次都要?jiǎng)?chuàng)建一個(gè)工廠太費(fèi)勁了,我們把工廠的抽象去掉,創(chuàng)建的方法改成靜態(tài):
public class ComputerFactory {
public static <T extends Computer> T createComputer(Class<T> c) {
Computer computer = null;
try {
computer = (T) Class.forName(c.getName()).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return (T) computer;
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Computer comp=ComputerFactory.createComputer(InspirationComputer.class);
// AbsrtactComputerFactory compFactory = new ComputerFactory();
// Computer inspirationComputer = compFactory.createComputer(InspirationComputer.class);
// System.out.println(inspirationComputer.getComputerDesc() + "\n" + inspirationComputer.getModelNumber() + "\n"
// + inspirationComputer.getComputerPrice());
// Computer vostroComputer = compFactory.createComputer(VostroComputer.class);
// System.out.println(vostroComputer.getComputerDesc() + "\n" + vostroComputer.getModelNumber() + "\n"
// + vostroComputer.getComputerPrice());
System.out.println(comp.getComputerDesc() + "\n" + comp.getModelNumber() + "\n"
+ comp.getComputerPrice());
}
現(xiàn)在是不是清爽了許多,這種方式應(yīng)用還是很廣泛的,叫做簡單工廠模式,但是工廠類的擴(kuò)展就不那么方便了
2.多工廠模式:我們還是看代碼
public abstract class AbsrtactComputerFactoryzz {
public abstract Computer createComputer();}
public class InspirationFactory extends AbsrtactComputerFactoryzz {
@Override
public Computer createComputer() {
return new InspirationComputer();
}}
我們創(chuàng)建了抽象工廠類,工廠里的方法還是生產(chǎn)電腦,這一點(diǎn)不容置疑,就像多年前一個(gè)前輩說的,人在黑板上花圓,現(xiàn)在有人,黑板,圓三個(gè)對(duì)象,畫圓的方法應(yīng)該誰提供呢?很明顯是圓,圓包含半徑,圓心,自然是它;
由于是多工廠模式,自然各個(gè)工廠各司其職,在子類中寫死這個(gè)工廠干什么的就行了;
3.替代單例模式
故名思議,這是一種可以用工廠方式做到單例設(shè)計(jì)模式的一種工廠變體,其實(shí)這是不明智的,開發(fā)有開發(fā)的規(guī)矩,不能使用一些非正常手段去實(shí)現(xiàn)通常寫法,但是也要了解一下,我們看下代碼:
public class CommonFactoryTest {
private static CommonFactoryTest singleton;
static {
try {
Class cl = Class.forName(CommonFactoryTest.class.getName());
// 獲得無參構(gòu)造
Constructor constructor = cl.getDeclaredConstructor();
// 設(shè)置無參構(gòu)造是可訪問的
constructor.setAccessible(true);
// 產(chǎn)生一個(gè)實(shí)例對(duì)象
singleton = (CommonFactoryTest) constructor.newInstance();
} catch (Exception e) {
// 異常處理
}
}
public static CommonFactoryTest getSingleton() {
return singleton;
}}
我們用暴力反射技術(shù),和classload的機(jī)制保證內(nèi)存中只有一個(gè)實(shí)例存在,但是很明顯效率是相當(dāng)?shù)偷?,不?yīng)該這么干。
4.延遲初始化,
一個(gè)對(duì)象被消費(fèi)完畢后,并不立刻釋放,工廠類保持其初始狀態(tài),等待再次被使用;
比如說我們不希望一個(gè)對(duì)象頻繁銷毀,想復(fù)用時(shí)候,比如做內(nèi)存緩存的時(shí)候,但是應(yīng)當(dāng)注意內(nèi)存泄漏的可能;比如Android開發(fā)中首頁tab的fragment我們就可以用這種方式保存其產(chǎn)品的狀態(tài)不被釋放以利復(fù)用;下面看一下代碼:
public static final int PLAYSHARE_SFRAGMENT = 0;
public static final int DISCUSS_FRAGMENT = 1;
public static final int MARKET_FRAGMENT = 2;
public static final int CONSULTING = 3;
public static final int MY_FRAGMENT = 4;
private static SparseArray<BaseFragment> mFragments = new SparseArray<BaseFragment>();
public static BaseFragment createFragment(int position) {
BaseFragment fragment = mFragments.get(position);
if (fragment == null) {
switch (position) {
case PLAYSHARE_SFRAGMENT:
fragment = new PlaySharesFragment();
break;
case DISCUSS_FRAGMENT:
fragment = new DiscussFragment();
break;
case MARKET_FRAGMENT:
fragment = new QuotationFragment();
break;
case CONSULTING:
fragment = new ConsultingFragment();
break;
case MY_FRAGMENT:
fragment = new MyFragment();
break;
default:
break;
}
mFragments.put(position, fragment);
}
return fragment;
}
很明顯,代碼對(duì)我們需要的產(chǎn)品進(jìn)行了緩存再利用;無論你用Map容器也好還是其他List集合也好,原理都一樣都是希望對(duì)已有產(chǎn)品的再利用,這個(gè)方式應(yīng)用也很廣泛,但是應(yīng)當(dāng)注意緩存的釋放;
工廠方法模式總結(jié):
是典型的解耦框架,高層模塊只需要知道產(chǎn)品的抽象類,其他的實(shí)
現(xiàn)類都不用關(guān)心,符合迪米特法則,我不需要的就不要去交流;也符合依賴倒置原則,只依
賴產(chǎn)品類的抽象;當(dāng)然也符合里氏替換原則,使用產(chǎn)品子類替換產(chǎn)品父類
使用場景:
萬物皆對(duì)象,工廠方法模式是用來生產(chǎn)對(duì)象,他可以替代new Object ,任何使用 new 對(duì)象的地方我們都可以用工廠模式替代,但是我們要考慮是否值得這么做,因?yàn)檫@樣對(duì)于開發(fā)者不也是增加了代碼復(fù)雜度嗎?
Ok今天就先到這里了,