重學(xué)設(shè)計(jì)模式之工廠模式

工廠模式是最常用的一類創(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ì)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,556評論 3 418
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,463評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,778評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,218評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,436評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,969評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,795評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,993評論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,229評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,687評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,990評論 2 374

推薦閱讀更多精彩內(nèi)容

  • 設(shè)計(jì)模式匯總 一、基礎(chǔ)知識(shí) 1. 設(shè)計(jì)模式概述 定義:設(shè)計(jì)模式(Design Pattern)是一套被反復(fù)使用、多...
    MinoyJet閱讀 3,960評論 1 15
  • 簡單工廠模式雖然簡單,但存在一個(gè)很嚴(yán)重的問題。當(dāng)系統(tǒng)中需要引入新產(chǎn)品時(shí),由于靜態(tài)工廠方法通過所傳入?yún)?shù)的不同來創(chuàng)建...
    justCode_閱讀 1,196評論 1 9
  • 一、設(shè)計(jì)模式的分類 總體來說設(shè)計(jì)模式分為三大類: 創(chuàng)建型模式,共五種:工廠方法模式、抽象工廠模式、單例模式、建造者...
    RamboLI閱讀 767評論 0 1
  • 總是在不經(jīng)意間 在誰的身上看到你的影子 每次都惹我懷念 反復(fù)的你的笑臉 那么逼真 或許這便是一個(gè)世界的缺點(diǎn) 或許也...
    林若玄葉閱讀 577評論 14 52
  • 《烏合之眾》由法國社會(huì)心理學(xué)家古斯塔夫·勒龐所寫,有意思的是該作者的本業(yè)是醫(yī)生,心理學(xué)只是他的業(yè)余愛好,而此書...
    江仔_閱讀 961評論 1 3