工廠模式理解了沒有?

前言

只有光頭才能變強(qiáng)

回顧前面:

昨天寫了單例模式了,今天是時(shí)候?qū)懝S模式啦~

工廠模式我個(gè)人認(rèn)為其實(shí)比較難理解的,如果有接觸過|聽過|見過該模式的同學(xué)很可能就會(huì)想:我自己new一個(gè)對(duì)象出來就好了,簡(jiǎn)單快捷。用得著你這個(gè)工廠模式嗎?搞一個(gè)工廠出來還要寫一大堆的代碼呢~

網(wǎng)上的很多資料都是在闡述著:工廠模式的好處就是解耦。相信大家對(duì)解耦這個(gè)詞也不陌生,那解耦究竟有什么好處呢?

本文章試圖去解釋為什么要用工廠模式,用了工廠模式的好處是什么,以及工廠模式衍生出的三種形式究竟有什么區(qū)別~~

那么接下來就開始吧,如果有錯(cuò)的地方希望能多多包涵,并不吝在評(píng)論區(qū)指正!

一、工廠模式概述

在《設(shè)計(jì)模式之禪》這本書中分了兩章節(jié)講工廠模式:

  • 工廠方法模式
    • (ps:其中里面講到了簡(jiǎn)單工廠模式)
  • 抽象工廠模式

網(wǎng)上的大部分資料都是將工廠模式分成種:

  • 簡(jiǎn)單/靜態(tài)工廠模式
  • 工廠方法模式
  • 抽象工廠模式

看完上面的敘述是不是想打死我,什么鳥玩意?不急哈,下面我會(huì)一一講到~~

1.1為什么要用工廠模式?

想想我們?yōu)槭裁匆霉S模式?下面我就簡(jiǎn)單舉例子:

文件IO的操作我們會(huì)經(jīng)常用得到吧,所以BufferedReader對(duì)象經(jīng)常要?jiǎng)?chuàng)建的:


    // 創(chuàng)建一個(gè)BufferedReader對(duì)象
    BufferedReader bf = new BufferedReader(new FileReader(new File("aa.txt")));

你說麻煩嗎?其實(shí)也不麻煩,就一行代碼嘛,哪里麻煩了~如果不太熟悉IO流的同學(xué)就沒有那么機(jī)靈了,創(chuàng)建一個(gè)BufferedReader可能就是以下的代碼了:


        File file = new File("aa.txt");
        FileReader fileReader = new FileReader(file);
        BufferedReader bufferedReader = new BufferedReader(fileReader);

你說麻煩嗎?其實(shí)也不麻煩,不就是三行代碼嘛,哪里麻煩了~如果這個(gè)應(yīng)用很多的類上都用到了BufferedReader對(duì)象的話,那每個(gè)類都寫上這三行代碼了。那你說麻煩嗎?那肯定麻煩啊,還用想啊....

可以看出來,創(chuàng)建一個(gè)BufferReader對(duì)象里面需要一個(gè)FileReader對(duì)象,而FileReader對(duì)象又要File對(duì)象。那創(chuàng)建這個(gè)BufferReader對(duì)象還是比較麻煩的(代碼上看不麻煩,從構(gòu)造上看還是挺麻煩的)!

雖然比較麻煩,但我們還能用,能用就行!于是乎,我們就去寫代碼了,現(xiàn)在有三個(gè)類都要進(jìn)行文件的讀寫操作,于是他們就有這樣的代碼:


public class FileOperateA {

    public static void main(String[] args) throws FileNotFoundException {
        File file = new File("aa.txt");
        FileReader fileReader = new FileReader(file);
        BufferedReader bufferedReader = new BufferedReader(fileReader);

        // 讀寫文件....
    }
}
image

此時(shí):上頭說,我要換成LineNumberReader來讀寫,有這個(gè)需求!那我們作為一個(gè)寫代碼的,能怎么辦?很絕望也需要去完成呀。

  • 不熟悉IDE的小伙子就一個(gè)一個(gè)將BufferedReader改成LineNumberReader,現(xiàn)在就3個(gè)類用到了BufferedReader,也就改6次而已。(ps:那如果很多地方都用到了呢?)
  • 熟悉IDE的小伙子就全局替換重構(gòu),妥妥的!

哎,寫個(gè)代碼屁事真多...那有沒有一種方法能夠讓創(chuàng)建對(duì)象變得簡(jiǎn)單而且修改對(duì)象時(shí)能很方便呢?

  • 哎,工廠模式就行了。

再說從面向?qū)ο蟮慕嵌葋砜?/strong>:我一個(gè)操作文件的類還要我會(huì)創(chuàng)建BufferReader是不是有點(diǎn)過分了?(職責(zé)沒有分工好)

  • 交給工廠來創(chuàng)建對(duì)象這就很面向?qū)ο罅耍?/li>

1.2體驗(yàn)工廠模式

何為工廠?將我們的產(chǎn)品都交由工廠來生產(chǎn)!我現(xiàn)在用的iphone5s,從哪來?從富士康組裝而來,富士康是工廠。我用得著知道iphone5s在富士康是怎么組裝起來的嗎?不需要。

來,我們來改造一下上面的例子。首先我們創(chuàng)建一個(gè)工廠類,它可以生產(chǎn)Reader對(duì)象!


// 創(chuàng)建Reader對(duì)象的工廠
public class ReaderFactory {
    public static Reader getReader() throws FileNotFoundException {
        File file = new File("aa.txt");
        FileReader fileReader = new FileReader(file);
        BufferedReader reader = new BufferedReader(fileReader);
        return reader;
    }
}

那么我們要得到BufferReader對(duì)象就賊簡(jiǎn)單了:


public class FileOperateA {

    public static void main(String[] args) throws FileNotFoundException {

        //-------我有工廠了,還用自己搞嗎?不用了!
        //File file = new File("aa.txt");
        //FileReader fileReader = new FileReader(file);
        //BufferedReader bufferedReader = new BufferedReader(fileReader);
        //-------我有工廠了,還用自己搞嗎?不用了!

        // 用工廠來創(chuàng)建出對(duì)象
        Reader reader = ReaderFactory.getReader();

        // 讀寫文件....
    }
}

工廠將我們創(chuàng)建的對(duì)象過程給屏蔽了

此時(shí)我要改成LineNumberReader怎么玩?在工廠上改一下就好了:

image

我們的調(diào)用方FileOperateA|FileOperateB|FileOperateC這些類完全就不用變!

1.3使用工廠方法的好處

從上面的工廠模式體驗(yàn)我們就可以看到:

  • 我們修改了具體的實(shí)現(xiàn)類,對(duì)客戶端(調(diào)用方)而言是完全不用修改的。
  • 如果我們使用new的方式來創(chuàng)建對(duì)象的話,那么我們就說:new出來的這個(gè)對(duì)象和當(dāng)前客戶端(調(diào)用方)耦合了!
    • 也就是,當(dāng)前客戶端(調(diào)用方)依賴著這個(gè)new出來的對(duì)象!

這就是解耦的好處!

我再放下我之前練習(xí)的時(shí)候?qū)戇^的代碼吧:

我有一個(gè)DaoFactory,邏輯很簡(jiǎn)單就是專門創(chuàng)建Dao對(duì)象的~

image

那么在Service層就可以使用工廠將想要的Dao對(duì)象初始化了~

image

此時(shí)我們的Service與Dao的對(duì)象低耦合的~

  • 大家可能看不出有什么好處,還弄了一大堆的字符串啥的~~

在Service與Controller層我也弄了一個(gè)ServiceFactory,根據(jù)當(dāng)時(shí)業(yè)務(wù)的需要(添加權(quán)限),我創(chuàng)建Service時(shí)就非常靈活了:

image

二、如何使用工廠模式

在一開始我就說了,工廠模式可以分成三類:

  • 簡(jiǎn)單/靜態(tài)工廠模式
  • 工廠方法模式
  • 抽象工廠模式

下面我就逐一來介紹一下每一種工廠模式有什么不一樣~

三種模式都以:Java3y要買寵物的例子來講解~

2.1工廠方法模式

很多博客都是以簡(jiǎn)單/靜態(tài)工廠模式,工廠方法模式,抽象工廠模式這個(gè)順序來講解工廠模式的。我認(rèn)為按書上的順序比較好理解~因?yàn)楹?jiǎn)單/靜態(tài)工廠模式是在工廠方法模式上縮減,抽象工廠模式是在工廠方法模式上再增強(qiáng)。

  • 所以我就先講工廠方法模式了。

Java3y每天寫代碼很無聊,想要買只寵物來陪陪自己。于是乎就去寵物店看寵物啦~~~

作為一間寵物店,號(hào)稱什么寵物都有!于是乎,店主宣傳的時(shí)候就說:我的寵物店什么寵物都有!

于是構(gòu)建寵物的工廠就誕生了~


// 號(hào)稱什么寵物都有
public interface AnimalFactory {

    // 可以獲取任何的寵物
    Animal createAnimal();
}

當(dāng)然了,主流的寵物得進(jìn)貨一些先放在店里充充門面,一些特殊的寵物就告訴顧客要時(shí)間進(jìn)貨~

  • 所以,我們就有了構(gòu)建貓和狗的工廠(繼承著所有寵物的工廠)

貓工廠:


// 繼承著寵物工廠
public class CatFactory implements AnimalFactory {
    @Override
    // 創(chuàng)建貓
    public Animal createAnimal() {
        return new Cat();
    }

}

狗工廠也是一樣的:


// 繼承著寵物工廠
public class DogFactory implements AnimalFactory {

    // 創(chuàng)建狗
    @Override
    public Animal createAnimal() {
        return new Dog();
    }

}

嗯,還有我們的實(shí)體類:貓、狗、動(dòng)物(多態(tài):貓和狗都是動(dòng)物,可以直接用動(dòng)物來表示了)

動(dòng)物實(shí)體類:


public abstract class Animal {

    // 所有的動(dòng)物都會(huì)吃東西
    public abstract void eat();
}

貓實(shí)體類:


public class Cat extends Animal {

    // 貓喜歡吃魚
    @Override
    public void eat() {
        System.out.println("貓吃魚");
    }

}

狗實(shí)體類:


public class Dog extends Animal {

    // 狗喜歡吃肉
    @Override
    public void eat() {
        System.out.println("狗吃肉");
    }

}

那么現(xiàn)在Java3y想要一只狗,跟了寵物店老板說,寵物店老板就去找狗回來了:


    // 去找狗工廠拿一只狗過來
    AnimalFactory f = new DogFactory();

    // 店主就拿到了一只狗給Java3y
    Animal a = f.createAnimal();
    a.eat();
    System.out.println("關(guān)注公眾號(hào):Java3y");

那么現(xiàn)在Java3y想要一只貓,跟了寵物店老板說,寵物店老板就去找貓回來了:


    // 去找貓工廠拿一只貓過來
    AnimalFactory ff = new CatFactory();

    // 店主就拿到了一只貓給Java3y
    Animal aa = ff.createAnimal();
    aa.eat();
    System.out.println("關(guān)注公眾號(hào):Java3y");

如果這個(gè)時(shí)候Java3y說想要一只蜥蜴怎么辦啊?沒問題啊,店主搞個(gè)蜥蜴工廠就好了~~


        // 要買蜥蜴..
        AnimalFactory fff = new LizardFactory();
        Animal aaa = ff.createAnimal();
        aaa.eat();

優(yōu)點(diǎn):

  • 1:客戶端不需要在負(fù)責(zé)對(duì)象的創(chuàng)建,明確了各個(gè)類的職責(zé)
  • 2:如果有新的對(duì)象增加,只需要增加一個(gè)具體的類和具體的工廠類即可
  • 3:不會(huì)影響已有的代碼,后期維護(hù)容易,增強(qiáng)系統(tǒng)的擴(kuò)展性

缺點(diǎn):

  • 1:需要額外的編寫代碼,增加了工作量

工廠方法類圖:

image

2.2簡(jiǎn)單/靜態(tài)工廠模式

現(xiàn)在寵物店生意不好做啊,號(hào)稱“什么寵物都有",這吹過頭了~~于是店主只賣兩種常見的寵物了。

  • 既然就只有兩種寵物的話,那就沒必要有”貓廠“、”狗廠“了,一個(gè)貓狗廠就行了!

所以我們的工廠是這樣子的:


public class AnimalFactory {
    public static Dog createDog() {
        return new Dog();
    }

    public static Cat createCat() {
        return new Cat();
    }

    // 外界想要貓要狗,這里創(chuàng)建就好了
    public static Animal createAnimal(String type) {
        if ("dog".equals(type)) {
            return new Dog();
        } else if ("cat".equals(type)) {
            return new Cat();
        } else {
            return null;
        }
    }
}

三個(gè)實(shí)體還是沒變(動(dòng)物、貓、狗)....

那么Java3y去寵物店買貓狗的時(shí)候,告訴老板我要貓、我要狗:


        // 拿到狗
        Animal A = AnimalFactory.createAnimal("dog");
        A.eat();

        // 拿到貓
        Animal C = AnimalFactory.createAnimal("cat");
        C.eat();

現(xiàn)在問題來了:

  • 1:我想要一個(gè)豬,可是我的工廠類沒有豬
  • 2:我就去代碼,寫可以創(chuàng)建豬對(duì)象的
  • 3:接著,我又要其他的動(dòng)物
  • 4:我還是得代碼
  • 5...................
  • 6:這就是簡(jiǎn)單工廠類的缺點(diǎn):當(dāng)需求改變了,我就要改代碼.

簡(jiǎn)單工廠類的優(yōu)點(diǎn)也很明顯:我就一個(gè)具體的工廠來創(chuàng)建對(duì)象,代碼量少。

2.3抽象工廠模式

抽象工廠模式就比較復(fù)雜了,我們一般的應(yīng)用都寫不到。我首先來簡(jiǎn)述一下需求吧:

  • 現(xiàn)在非常流行在貓狗屆也吹起了一股“性別風(fēng)”
    • 有的喜歡公的
    • 有的喜歡母的

那我們的貓和狗都是有性別的,不是公的就是母的~~

  • 我們之前在工廠方法模式下是每個(gè)動(dòng)物都開一個(gè)工廠,如果動(dòng)物過多的話,那么就有很多的工廠~
  • 那現(xiàn)在我們可以抽取出來:每個(gè)動(dòng)物不是公的就是母的~
  • 所以我們有兩個(gè)工廠就足夠了!

具體的代碼是這樣的:

我們的最大工廠還是定義了創(chuàng)建什么動(dòng)物


public interface AnimalFactory {
    Animal createDog();
    Animal createCat();
}

創(chuàng)建母貓和母狗的工廠:


public class FemaleAnimalFactory implements AnimalFactory {

    // 生產(chǎn)母狗和母貓
    @Override
    public Animal createDog() {
        return  new FemaleDog();
    }

    @Override
    public Animal createCat() {
        return new FemaleCat();
    }

}

創(chuàng)建公貓和公狗的工廠:


public class MaleAnimalFactory implements AnimalFactory {

    // 生產(chǎn)公狗和公貓

    @Override
    public Animal createDog() {
        return new MaleDog();
    }

    @Override
    public Animal createCat() {
        return new MaleCat();
    }

}

這是所有動(dòng)物都擁有的普遍行為


public abstract class Animal {

    // 所有的動(dòng)物都會(huì)吃東西
    public abstract void eat();

    // 所有的動(dòng)物都有性別
    public abstract void gender();
}

這是貓都擁有的普遍行為:


public abstract class Cat extends Animal {
    // 貓喜歡吃魚
    @Override
    public void eat() {
        System.out.println("貓吃魚");
    }
}

這是狗都擁有的普遍行為:

public abstract class Dog extends Animal {

    // 狗喜歡吃肉
    @Override
    public void eat() {
        System.out.println("狗吃肉");
    }

}

貓分為公貓、母貓。狗分為公狗和母狗:

public class FemaleCat extends Cat {

    public void gender() {
        System.out.println("I am a female Cat");
    }

}

.....

image

簡(jiǎn)單來說:工廠方法模式的工廠是創(chuàng)建出一種產(chǎn)品,而抽象工廠是創(chuàng)建出一類產(chǎn)品。

  • 一類的產(chǎn)品我們稱之為產(chǎn)品族
    • 貓是一類的,狗也是一類的。所以AnimalFactory定義了兩類產(chǎn)品--->Animal createDog();Animal createCat();
  • 產(chǎn)品的繼承結(jié)構(gòu)稱之為產(chǎn)品等級(jí)。
    • 所有的動(dòng)物都是會(huì)吃東西的,它們都是有性別的,這是最普遍的。所以Animal定義了兩個(gè)抽象方法:public abstract void eat();public abstract void gender();
    • 所有的狗都是會(huì)吃肉的,所以Dog實(shí)現(xiàn)了eat()方法
      • 狗又分成了公狗和母狗,所以定義了兩個(gè)類FemaleDog和MaleDog繼承了Dog,實(shí)現(xiàn)了gender()方法
    • 所有的貓都是會(huì)吃魚的,所以Cat實(shí)現(xiàn)了eat()方法
      • 貓又分成了公貓和母貓,所以定義了兩個(gè)類FemaleCat和MaleCat繼承了Cat,實(shí)現(xiàn)了gender()方法
  • 具體的工廠是面向多個(gè)產(chǎn)品等級(jí)結(jié)構(gòu)進(jìn)行生產(chǎn)。
    • 所以FemaleAnimalFactory定義了createDog()createCat()生產(chǎn)母狗和母貓
    • 所以MaleAnimalFactory定義了createDog()createCat()生產(chǎn)公狗和共貓
  • 找到母工廠就可以創(chuàng)建母貓和母狗,找到公工廠就可以創(chuàng)建公貓和公狗
image

    public static void main(String[] args) {

        // 需要性別為母的就去找母工廠
        AnimalFactory af = new FemaleAnimalFactory();

        // 需要一只母貓
        af.createCat().gender();

        // 需要一只母狗
        af.createDog().gender();

        System.out.println("-------------關(guān)注公眾號(hào):Java3y-------------------------");

        // 需要性別為公的就去找公工廠
        AnimalFactory aff = new MaleAnimalFactory();

        // 需要一只公狗
        aff.createDog().gender();

        // 需要一只公貓
        aff.createCat().gender();

    }

效果:

image

這是抽象工廠模式的類圖:

image

抽象工廠模式說到底就是多了一層抽象,減少了工廠的數(shù)量

抽象工廠缺點(diǎn)也很明顯:

  • 難以擴(kuò)展產(chǎn)品族--->如果我再要寵物豬的話
    • 那我要修改AnimalFactory、FemaleAnimalFactory、MaleAnimalFactory這些類了~

三、總結(jié)

總的來說我們用簡(jiǎn)單工廠模式比較多,工廠方式模式的話代碼量會(huì)比較大,抽象工廠模式的話需要業(yè)務(wù)比較大的情況下才會(huì)用到(如果有更好的理解方式不妨在評(píng)論區(qū)留言,一起交流交流漲漲見識(shí)~~)

  • 工廠模式配合反射來使用也是極好的~

參考資料:

如果文章有錯(cuò)的地方歡迎指正,大家互相交流。習(xí)慣在微信看技術(shù)文章,想要獲取更多的Java資源的同學(xué),可以關(guān)注微信公眾號(hào):Java3y。為了大家方便,剛新建了一下qq群:742919422,大家也可以去交流交流。謝謝支持了!希望能多介紹給其他有需要的朋友

文章的目錄導(dǎo)航

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 設(shè)計(jì)模式匯總 一、基礎(chǔ)知識(shí) 1. 設(shè)計(jì)模式概述 定義:設(shè)計(jì)模式(Design Pattern)是一套被反復(fù)使用、多...
    MinoyJet閱讀 4,094評(píng)論 1 15
  • 1.ios高性能編程 (1).內(nèi)層 最小的內(nèi)層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結(jié)構(gòu)(3).初始化時(shí)...
    歐辰_OSR閱讀 30,264評(píng)論 8 265
  • 車險(xiǎn)處理步驟:第一步:撥打相關(guān)電話。 1、救人電話:120 2、定責(zé)電話:122 3、定損電話:400869551...
    黃祥閱讀 270評(píng)論 0 0
  • 世間的感情莫過于兩種: 一種是相濡以沫, 卻厭倦到終老。 另一種是相忘于江湖, 卻懷念到哭泣! ...
    素箋姑娘閱讀 121評(píng)論 0 0
  • 見字如晤。 很高興你還在等待我的更新,今天是整整一年的時(shí)間,是值得紀(jì)念的時(shí)間。相比于一年前,我少了那種欣喜的激動(dòng),...
    美其名曰易非凡閱讀 698評(píng)論 7 1

友情鏈接更多精彩內(nèi)容