工廠模式是最常用的一類創(chuàng)建型設(shè)計(jì)模式,之前一直以為工廠模式只是23中設(shè)計(jì)模式中的一種,重新了解才知道這個(gè)模式還要細(xì)分為三種工廠模式:簡單工廠模式、工廠方法模式、抽象工廠模式。這三個(gè)工廠模式各有特色,難度也逐個(gè)加大,在軟件開發(fā)中它們都得到了廣泛的應(yīng)用。
有意思的是,我以為對于這三種工廠模式的理解應(yīng)該都是一樣,但是閱讀了N篇關(guān)于工廠模式的博文后,發(fā)現(xiàn)他們好多關(guān)于這三個(gè)工廠模式的定義都不一致,有些人定義的抽象工廠模式卻是另一個(gè)人定義的工廠方法模式,讓人很是糾結(jié)啊??。
從眾多的博文中我推薦劉偉技術(shù)博客,雖然博客比較早,但是關(guān)于設(shè)計(jì)模式的博客內(nèi)容真的很詳細(xì),值得學(xué)習(xí)!而且他的每一篇博客后基本上都留有一個(gè)練習(xí)題,我決定以他的練習(xí)題為入口來學(xué)習(xí)學(xué)習(xí)設(shè)計(jì)模式,源碼可在我的github上獲取。
簡單工廠模式(Simple Factory Pattern)
定義
定義一個(gè)工廠類,它可以根據(jù)參數(shù)的不同返回不同的實(shí)例,被創(chuàng)建的實(shí)例通常都具有共同的父類。因?yàn)樵诤唵喂S模式中用于創(chuàng)建實(shí)例的方法是靜態(tài)方法,因此簡單工廠模式又被稱為靜態(tài)工廠方法(Static Factory Method)模式,它屬于類創(chuàng)建型模式。
簡單說,就在當(dāng)你需要什么,傳入一個(gè)正確的參數(shù),就可以獲取你所需要的對象,而無需知道其創(chuàng)建細(xì)節(jié)。
實(shí)例
使用簡單工廠模式設(shè)計(jì)一個(gè)可以創(chuàng)建不同幾何形狀(如圓形、方形和三角形等)的繪圖工具,每個(gè)幾何圖形都具有繪制draw()和擦除erase()兩個(gè)方法。
結(jié)構(gòu)圖
根據(jù)簡單工廠模式設(shè)計(jì)的結(jié)構(gòu)圖如下(推薦一款在線繪制UML的軟件ProcessOn)
代碼
完整代碼如下
//圖形接口
public interface Shape {
void draw();
void erase();
}
//圓形
public class RoundShape implements Shape {
@Override
public void draw() {
System.out.println("畫了一個(gè)圓形!");
}
@Override
public void erase() {
System.out.println("擦除一個(gè)圓形!");
}
}
//三角形
public class TriangleShape implements Shape {
@Override
public void draw() {
System.out.println("畫了一個(gè)三角形!");
}
@Override
public void erase() {
System.out.println("擦除一個(gè)三角形!");
}
}
//工廠類
public class ShapeFactory {
public static Shape getShape(String type) {
Shape shape = null;
if (type.equalsIgnoreCase("ROUND")) {
shape = new RoundShape();
} else if (type.equalsIgnoreCase("TRIANGLE")) {
shape = new TriangleShape();
}
return shape;
}
}
客戶端測試代碼如下
public class Client {
public static void main(String[] args) {
Shape shape;
shape = ShapeFactory.getShape("round");
shape.draw();
shape.erase();
shape = ShapeFactory.getShape("triangle");
shape.draw();
shape.erase();
}
}
運(yùn)行結(jié)果如下
畫了一個(gè)圓形!
擦除一個(gè)圓形!
畫了一個(gè)三角形!
擦除一個(gè)三角形!
小結(jié)
簡單工廠模式中工廠類的職責(zé)太重,一旦不能工作將影響整個(gè)系統(tǒng),且如果添加過多的產(chǎn)品,工廠類中的邏輯就過于復(fù)雜。所以簡單工廠模式適用于邏輯簡單,且需要?jiǎng)?chuàng)建對象較少的情況。
工廠方法模式(Factory Method Pattern)
定義
定義一個(gè)用于創(chuàng)建對象的接口,讓子類決定將哪一個(gè)類實(shí)例化。工廠方法模式讓一個(gè)類的實(shí)例化延遲到其子類。工廠方法模式又簡稱為工廠模式(Factory Pattern),又可稱作虛擬構(gòu)造器模式(Virtual Constructor Pattern)或多態(tài)工廠模式(Polymorphic Factory Pattern)。工廠方法模式是一種類創(chuàng)建型模式。
工廠方法模式提供一個(gè)抽象工廠接口來聲明抽象工廠方法,而由其子類來具體實(shí)現(xiàn)工廠方法,針對不同的產(chǎn)品提供不同的工廠。
實(shí)例
使用工廠方法模式設(shè)計(jì)一個(gè)程序來讀取各種不同類型的圖片格式,針對每一種圖片格式都設(shè)計(jì)一個(gè)圖片讀取器,如GIF圖片讀取器用于讀取GIF格式的圖片、JPG圖片讀取器用于讀取JPG格式的圖片。需充分考慮系統(tǒng)的靈活性和可擴(kuò)展性。
結(jié)構(gòu)圖
根據(jù)工廠方法模式創(chuàng)建的結(jié)構(gòu)圖如下
從結(jié)構(gòu)圖可以知道,我們針對每一種讀取器都創(chuàng)建了一個(gè)對應(yīng)的工廠類,而所有的工廠類都繼承自一個(gè)抽象工廠方法,在Client中我們只需定義抽象工廠類,具體實(shí)現(xiàn)可自己選擇。
代碼
完整代碼如下
//讀取器的接口
public interface Reader {
void readPic();
}
//工廠類接口:抽象方法
public interface ReaderFactory {
Reader createReader();
}
//定義兩種讀取器
public class JpgReader implements Reader {
@Override
public void readPic() {
System.out.println("讀取JPG格式圖片!");
}
}
public class GifReader implements Reader {
@Override
public void readPic() {
System.out.println("讀取gif格式圖片");
}
}
//創(chuàng)建對應(yīng)的工廠方法
public class JpgReaderFactory implements ReaderFactory {
@Override
public Reader createReader() {
//初始化讀取器的細(xì)節(jié)
return new JpgReader();
}
}
public class GifReaderFactory implements ReaderFactory {
@Override
public Reader createReader() {
//初始化讀取器的細(xì)節(jié)
return new GifReader();
}
}
客戶端測試代碼如下
public class Client {
public static void main(String[] args) {
ReaderFactory readerFactory;
Reader reader;
readerFactory = new GifReaderFactory();
reader = readerFactory.createReader();
reader.readPic();
readerFactory = new JpgReaderFactory();
reader = readerFactory.createReader();
reader.readPic();
}
}
測試運(yùn)行結(jié)果如下
讀取gif格式圖片
讀取JPG格式圖片!
小結(jié)
工廠方法模式是對簡單工廠模式的升級應(yīng)用,它解決了簡單工廠模式中工廠類職責(zé)繁重的問題,且更符合設(shè)計(jì)模式的開閉原則,增加新的產(chǎn)品不需要修改原先的代碼,只需要增加對應(yīng)的產(chǎn)品實(shí)例及工廠實(shí)例即可,同時(shí)這也是工廠方法模式的一個(gè)缺點(diǎn),添加新產(chǎn)品,系統(tǒng)中的類就需要成對的增加,一定程度上增加了系統(tǒng)的復(fù)雜度。
在工廠方法模式中,客戶并不需要知道具體產(chǎn)品類創(chuàng)建的細(xì)節(jié),只需要關(guān)心產(chǎn)品對應(yīng)的工廠,根據(jù)工廠創(chuàng)建自己需要的產(chǎn)品就行了。
抽象工廠模式(Abstract Factory Pattern)
定義
提供一個(gè)創(chuàng)建一系列相關(guān)或相互依賴對象的接口,而無需指定它們具體的類。抽象工廠模式又稱為Kit模式,它是一種對象創(chuàng)建型模式。
光看定義相信大部分小伙伴跟我一樣是一臉懵逼,與簡單工廠或工廠方法模式相比,抽象工廠模式中的具體工廠不只是創(chuàng)建一種產(chǎn)品,它負(fù)責(zé)創(chuàng)建一族產(chǎn)品。
如上圖所示,我們之前學(xué)習(xí)的簡單工廠模式及工廠方法模式解決的都只是一種產(chǎn)品的創(chuàng)建過程,而在現(xiàn)實(shí)中,一個(gè)工廠往往不僅僅生產(chǎn)一種產(chǎn)品,例如惠普的工廠可能同時(shí)生產(chǎn)打印機(jī)、洗衣機(jī)、冰箱等產(chǎn)品,這里我們就需要對于一個(gè)產(chǎn)品族創(chuàng)建一個(gè)抽象工廠,在這個(gè)在具體的某一個(gè)產(chǎn)品族的工廠中創(chuàng)建具體的產(chǎn)品。
結(jié)構(gòu)圖如下
下面還是根據(jù)一個(gè)實(shí)例來理解抽象工廠模式
實(shí)例
Sunny軟件公司欲推出一款新的手機(jī)游戲軟件,該軟件能夠支持Symbian、Android和Windows Mobile等多個(gè)智能手機(jī)操作系統(tǒng)平臺(tái),針對不同的手機(jī)操作系統(tǒng),該游戲軟件提供了不同的游戲操作控制(OperationController)類和游戲界面控制(InterfaceController)類,并提供相應(yīng)的工廠類來封裝這些類的初始化過程。軟件要求具有較好的擴(kuò)展性以支持新的操作系統(tǒng)平臺(tái),為了滿足上述需求,試采用抽象工廠模式對其進(jìn)行設(shè)計(jì)。
結(jié)構(gòu)圖
根據(jù)抽象工廠模式定義設(shè)計(jì)結(jié)構(gòu)圖如下
根據(jù)需求我們知道,當(dāng)該游戲在不同的操作系統(tǒng)時(shí),需要分別使用不同的操作控制器和界面控制器,所以我們針對不同操作系統(tǒng)設(shè)計(jì)一個(gè)抽象工廠,該抽象工廠中定義兩個(gè)方法,分別生產(chǎn)操作控制器和界面控制器。然后生產(chǎn)不同系統(tǒng)的工廠類,在工廠類中生成對應(yīng)的操作控制器和界面控制器。
代碼
操作控制器代碼
//操作控制器接口
public interface OperationController {
void controlOperation();
}
//生產(chǎn)三種操作控制器接口
public class SymbianOperationController implements OperationController {
@Override
public void controlOperation() {
System.out.println("使用Symbian系統(tǒng)操作控制");
}
}
public class AndroidOperationController implements OperationController {
@Override
public void controlOperation() {
System.out.println("使用Android系統(tǒng)操作控制");
}
}
public class WindowsOperationController implements OperationController {
@Override
public void controlOperation() {
System.out.println("使用Windows Mobile系統(tǒng)操作控制");
}
}
界面控制器代碼
//界面控制器接口
public interface InterfaceController {
void controlInterface();
}
//分別生成三種界面控制器
public class SymbianInterfaceController implements InterfaceController {
@Override
public void controlInterface() {
System.out.println("使用Symbian系統(tǒng)游戲界面控制");
}
}
public class AndroidInterfaceController implements InterfaceController {
@Override
public void controlInterface() {
System.out.println("使用Android系統(tǒng)游戲界面控制");
}
}
public class WindowsInterfaceController implements InterfaceController {
@Override
public void controlInterface() {
System.out.println("使用Windows Mobile系統(tǒng)游戲界面控制");
}
}
抽象工廠代碼
//抽象工廠接口
public interface SysFactory {
InterfaceController createInterface();
OperationController createOperation();
}
//生產(chǎn)三種不同系統(tǒng)的抽象工廠
public class SymbianSysFactory implements SysFactory {
@Override
public InterfaceController createInterface() {
return new SymbianInterfaceController();
}
@Override
public OperationController createOperation() {
return new SymbianOperationController();
}
}
public class AndroidSysFactory implements SysFactory {
@Override
public InterfaceController createInterface() {
return new AndroidInterfaceController();
}
@Override
public OperationController createOperation() {
return new AndroidOperationController();
}
}
public class WindowsSysFactory implements SysFactory {
@Override
public InterfaceController createInterface() {
return new WindowsInterfaceController();
}
@Override
public OperationController createOperation() {
return new WindowsOperationController();
}
}
客戶端測試代碼
public class Client {
public static void main(String[] args) {
SysFactory sysFactory;
InterfaceController interfaceController;
OperationController operationController;
sysFactory = new SymbianSysFactory();
interfaceController = sysFactory.createInterface();
operationController = sysFactory.createOperation();
interfaceController.controlInterface();
operationController.controlOperation();
}
}
測試結(jié)果如下
使用Symbian系統(tǒng)游戲界面控制
使用Symbian系統(tǒng)操作控制
當(dāng)我們需要切換不同的操作系統(tǒng)時(shí),只需要修改
sysFactory = new AndroidSysFactory();
結(jié)果就是
使用Android系統(tǒng)游戲界面控制
使用Android系統(tǒng)操作控制
小結(jié)
抽象工廠模式是對工廠方法模式的進(jìn)一步升級使用,使得工廠方式更符合實(shí)際開發(fā)需求,在實(shí)際開發(fā)中往往需要一個(gè)產(chǎn)品族來完成工作。抽象工廠模式在新增產(chǎn)品族時(shí)很方便,無需修改已有系統(tǒng),符合“開閉原則”。但抽象工廠模式最大的缺點(diǎn)是對于新增產(chǎn)品很麻煩,需要對原有系統(tǒng)進(jìn)行較大的修改,不符合“開閉原則”。所以在實(shí)際開發(fā)中,我們需要衡量具體的需求,選擇對應(yīng)的設(shè)計(jì)模式,對于需要客戶端使用同一產(chǎn)品族的需求時(shí),可以選擇抽象工廠模式進(jìn)行設(shè)計(jì)。