略懂設(shè)計(jì)模式之工廠模式

前言

工廠模式應(yīng)該是大家的老朋友了,相信很多朋友在學(xué)習(xí)和工作中一定遇到過(guò),但是不一定很了解,這篇文章將通過(guò)幾個(gè)例子蛤高,帶大家一起進(jìn)一步了解工廠模式。

簡(jiǎn)介

工廠模式(Factory Pattern)是 Java 中最常用的設(shè)計(jì)模式之一。 這種類(lèi)型的設(shè)計(jì)模式屬于創(chuàng)建型模式 襟齿,它提供了一種創(chuàng)建對(duì)象的最佳方式姻锁。

在創(chuàng)建型模式中工廠模式是比較重要的一種枕赵,之所以名稱(chēng)中包含“工廠”二字猜欺,是因?yàn)橛霉S代替了 new 操作,將對(duì)象實(shí)例化的過(guò)程交給工廠來(lái)實(shí)現(xiàn)拷窜。

工廠模式關(guān)心的是最終創(chuàng)建的對(duì)象, 而不關(guān)心創(chuàng)建的過(guò)程开皿。 舉個(gè)例子,好比您需要一輛汽車(chē)篮昧,可以直接從工廠里面提貨赋荆,而不用去管這輛汽車(chē)是怎么做出來(lái)的,以及這個(gè)汽車(chē)?yán)锩娴木唧w實(shí)現(xiàn)懊昨。

工廠模式可以分為三類(lèi):

  • 簡(jiǎn)單工廠模式 (Simple Factory)

  • 工廠方法模式 (Factory Method)

  • 抽象工廠模式 (Abstract Factory)

其中簡(jiǎn)單工廠模式并不屬于23種 GOF 設(shè)計(jì)模式之一窄潭,而是將其看作工廠方法模式的一種特例,兩者歸為一類(lèi)酵颁。

簡(jiǎn)單工廠模式

簡(jiǎn)單工廠模式又叫靜態(tài)工廠模式嫉你,由一個(gè)工廠類(lèi)根據(jù)傳入的參數(shù),動(dòng)態(tài)決定應(yīng)該創(chuàng)建哪一個(gè)產(chǎn)品類(lèi)(繼承自一個(gè)父類(lèi)或接口)的實(shí)例躏惋。

簡(jiǎn)單工廠模式的主要組成:

工廠(Factory): 負(fù)責(zé)實(shí)現(xiàn)創(chuàng)建所有實(shí)例的內(nèi)部邏輯幽污,并提供一個(gè)外界調(diào)用的方法,創(chuàng)建所需的產(chǎn)品對(duì)象

抽象產(chǎn)品(Product): 負(fù)責(zé)描述產(chǎn)品的公共接口

具體產(chǎn)品(ConcreteProduct): 描述生產(chǎn)的具體產(chǎn)品

這里我們就以生產(chǎn)汽車(chē)為例:

img

首先定義產(chǎn)品簿姨,先想好要生產(chǎn)什么:

// 汽車(chē)基類(lèi)
public abstract class Car {
    // 輸出汽車(chē)信息
    public abstract void printInfo();
}

// 比亞迪汽車(chē)
public class BydCar extends Car{
    @Override
    public void printInfo() {
        System.out.println("這是比亞迪汽車(chē)");
    }
}

// 吉利汽車(chē)
public class GeelyCar extends Car{
    @Override
    public void printInfo() {
        System.out.println("這是吉利汽車(chē)");
    }
}

然后定義工廠類(lèi)距误,想好生產(chǎn)什么產(chǎn)品之后,就要建工廠了:

// 汽車(chē)工廠類(lèi)
public class CarFactory {
    // 生產(chǎn)汽車(chē)
    public static Car productionCar(String brand) {
        if ("geely".equals(brand)) {
            return new GeelyCar();
        } else if ("byd".equals(brand)) {
            return new BydCar();
        } else {
            return null;
        }
    }
}

工廠建好了扁位,有了比亞迪和吉利兩條生產(chǎn)線准潭,就可以大膽生產(chǎn)汽車(chē)了:

public class FactoryPatternDemo {
    public static void main(String[] args) {
        Car car = CarFactory.productionCar("byd");
        car.printInfo();
    }
}
// 輸出:這是比亞迪汽車(chē)

是不是很簡(jiǎn)單,這時(shí)候可能有朋友就要問(wèn)了域仇,那我想要紅旗汽車(chē)怎么辦刑然,還得再創(chuàng)建一個(gè)紅旗汽車(chē)類(lèi),然后修改工廠類(lèi)的判斷邏輯殉簸,顯然這是違背開(kāi)閉原則的闰集。

那么可不可以不修改工廠類(lèi)里的邏輯呢?

當(dāng)然可以般卑,還有一種方式是通過(guò)反射來(lái)創(chuàng)建具體的產(chǎn)品武鲁,我們熟悉的 Spring 的 BeanFactory 就是采用反射的方式實(shí)現(xiàn)的。

還是汽車(chē)工廠類(lèi)蝠检,我們修改一下代碼:

public class CarFactory {
    public static Car productionCar(Class c){
        Car car = null;
        try {
            car = (Car) Class.forName(c.getName()).newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return car;
    }
}

反射的方式看起來(lái)代碼簡(jiǎn)潔多了是吧沐鼠,但是某些情況下并不合適,而且反射對(duì)程序性能也會(huì)有影響。

簡(jiǎn)單工廠模式適用于業(yè)務(wù)簡(jiǎn)單的情況下饲梭,而對(duì)于復(fù)雜的業(yè)務(wù)環(huán)境可能就不太適用了乘盖。這個(gè)時(shí)候就要工廠方法模式登場(chǎng)了。

工廠方法模式

在簡(jiǎn)單工廠模式中憔涉,工廠負(fù)責(zé)所有產(chǎn)品的生產(chǎn)订框,就像上面例子中,一個(gè)汽車(chē)工廠負(fù)責(zé)所有汽車(chē)的生產(chǎn)兜叨。而工廠方法模式則是將工廠類(lèi)抽象化穿扳,把生成具體產(chǎn)品的任務(wù)分發(fā)給繼承抽象方法的具體的產(chǎn)品工廠。

工廠方法模式的主要組成:

抽象工廠(Abstract Factory):描述具體工廠的公共接口

具體工廠(ConcreteFactory):描述具體工廠国旷,創(chuàng)建產(chǎn)品的實(shí)例矛物,供外界調(diào)用

抽象產(chǎn)品(Product):負(fù)責(zé)描述產(chǎn)品的公共接口

具體產(chǎn)品(ConcreteProduct):描述生產(chǎn)的具體產(chǎn)品

我們還是以生產(chǎn)汽車(chē)為例:

img

將簡(jiǎn)單工廠方法中的工廠類(lèi)改為抽象工廠類(lèi),再創(chuàng)建具體工廠類(lèi)來(lái)實(shí)現(xiàn)汽車(chē)的生產(chǎn)工作:

// 抽象汽車(chē)工廠類(lèi)
public abstract class AbstractCarFactory {
    // 生產(chǎn)汽車(chē)
    abstract Car productionCar();
}

// 比亞迪汽車(chē)工廠
public class BydCarFactory extends AbstractCarFactory{
    @Override
    Car productionCar()
        return new BydCar();
    }
}

// 吉利汽車(chē)工廠
public class GeelyCarFactory extends AbstractCarFactory{
    @Override
    Car productionCar()
        return new GeelyCar();
    }
}

這下要生產(chǎn)什么品牌的汽車(chē)跪但,就要交給具體的工廠了:

public class FactoryPatternDemo {
    public static void main(String[] args) {
        AbstractCarFactory factory = new BydCarFactory();
        Car car = factory.productionCar();
        car.printInfo();
    }
}
// 輸出:這是比亞迪汽車(chē)

工廠方法模式看起來(lái)要比簡(jiǎn)單工廠模式更復(fù)雜一些履羞,每增加一個(gè)新的產(chǎn)品就要增加一個(gè)工廠棱烂,但是他提高了系統(tǒng)的可擴(kuò)展性和可維護(hù)性册踩,完全符合開(kāi)閉原則欲侮。

我們可能遇到的大部分業(yè)務(wù)需求使用工廠方法模式足以應(yīng)付墨状,但是凡事都有特殊情況虾攻。當(dāng)產(chǎn)品種類(lèi)更加復(fù)雜芍殖,存在產(chǎn)品族的時(shí)候熄驼,就要使用抽象工廠模式了郭蕉。

抽象工廠模式

在介紹抽象工廠模式前蛤售,我們先了解下產(chǎn)品族是什么:位于不同產(chǎn)品等級(jí)結(jié)構(gòu)中丁鹉,功能相關(guān)聯(lián)的產(chǎn)品組成的家族。

沒(méi)有理解的話可以看下圖:

img

圖中悴能,比亞迪的商務(wù)汽車(chē)和吉利的商務(wù)汽車(chē)都屬于商務(wù)車(chē)產(chǎn)品族揣钦,運(yùn)動(dòng)車(chē)產(chǎn)品族同理。

抽象工廠模式提供了一種方式漠酿,可以將同一產(chǎn)品族的單獨(dú)的工廠封裝起來(lái)冯凹。它是三種工廠模式里面最為抽象、最具一般性的炒嘲。

抽象工廠模式和工廠方法模式一樣宇姚,都符合開(kāi)閉原則。但是不同的是夫凸,工廠方法模式在增加一個(gè)具體產(chǎn)品的時(shí)候浑劳,都要增加對(duì)應(yīng)的工廠。但是抽象工廠模式只有在新增一個(gè)類(lèi)型的具體產(chǎn)品時(shí)才需要新增工廠夭拌。也就是說(shuō)魔熏,工廠方法模式的一個(gè)工廠只能創(chuàng)建一個(gè)具體產(chǎn)品衷咽。而抽象工廠模式的一個(gè)工廠可以創(chuàng)建屬于一類(lèi)類(lèi)型的多種具體產(chǎn)品。工廠創(chuàng)建產(chǎn)品的個(gè)數(shù)介于簡(jiǎn)單工廠模式和工廠方法模式之間蒜绽。

抽象工廠模式的主要組成:

抽象工廠(AbstractFactory):描述具體工廠的公共接口

具體工廠(ConcreteFactory):描述具體工廠镶骗,創(chuàng)建產(chǎn)品的實(shí)例,供外界調(diào)用

抽象產(chǎn)品(族)(AbstractProduct):描述抽象產(chǎn)品的公共接口

具體產(chǎn)品(ConcreteProduct):描述具體產(chǎn)品的公共接口

同樣以生產(chǎn)汽車(chē)為例:

img

創(chuàng)建商務(wù)汽車(chē)產(chǎn)品族和運(yùn)動(dòng)汽車(chē)產(chǎn)品族相關(guān)類(lèi):

// 商務(wù)汽車(chē)抽象類(lèi)
public abstract class BusinessCar {
    public abstract void printInfo();
}

// 比亞迪商務(wù)汽車(chē)
public class BydBusinessCar extends BusinessCar{
    @Override
    public void printInfo() {
        System.out.println("這是比亞迪商務(wù)汽車(chē)");
    }
}

// 吉利商務(wù)汽車(chē)
public class GeelyBusinessCar extends BusinessCar{
    @Override
    public void printInfo(){
        System.out.println("這是吉利商務(wù)汽車(chē)");
    }
}

// 運(yùn)動(dòng)汽車(chē)抽象類(lèi)
public abstract class SportCar {
    public abstract void printInfo();
}

// 比亞迪運(yùn)動(dòng)汽車(chē)
public class BydSportCar extends SportCar{
    @Override
    public void printInfo()
 {
        System.out.println("這是比亞迪運(yùn)動(dòng)汽車(chē)");
    }
}

// 吉利運(yùn)動(dòng)汽車(chē)
public class GeelySportCar extends SportCar{
    @Override
    public void printInfo()
{
        System.out.println("這是吉利運(yùn)動(dòng)汽車(chē)");
    }
}

創(chuàng)建抽象工廠類(lèi)和具體工廠類(lèi):

// 抽象汽車(chē)工廠類(lèi)
public abstract class AbstractCarFactory {
    // 生產(chǎn)商務(wù)汽車(chē)
    abstract BusinessCar productionBusinessCar();
    // 生產(chǎn)運(yùn)動(dòng)汽車(chē)
    abstract SportCar productionSportCar();
}

// 比亞迪汽車(chē)工廠
public class BydCarFactory extends AbstractCarFactory{
    @Override
    public BusinessCar productionBusiness() {
        return new BydBusinessCar();
    }
    
    @Override
    public SportCar productionSport() {
        return new BydSportCar();
    }
}

// 吉利汽車(chē)工廠
public class GeelyCarFactory extends AbstractCarFactory{
    @Override
    public BusinessCar productionBusiness() {
        return new GeelyBusinessCar();
    }
    
    @Override
    public SportCar productionSport() {
        return new GeelySportCar();
    }
}

開(kāi)始生產(chǎn)汽車(chē):

public class FactoryPatternDemo {
    public static void main(String[] args) {
        AbstractCarFactory factory = new BydCarFactory();
        BusinessCar car = factory.BydBusinessCar();
        car.printInfo();
    }
}
// 輸出:這是比亞迪商務(wù)汽車(chē)

抽象工廠模式除了具有工廠方法模式的優(yōu)點(diǎn)外躲雅,最主要的優(yōu)點(diǎn)就是可以在類(lèi)的內(nèi)部對(duì)產(chǎn)品族進(jìn)行約束鼎姊。但是產(chǎn)品族的擴(kuò)展將是一件十分費(fèi)力的事情,假如產(chǎn)品族中需要增加一個(gè)新的產(chǎn)品吏夯,則幾乎所有的工廠類(lèi)都需要進(jìn)行修改此蜈,不能很好地支持開(kāi)閉原則即横。所以使用抽象工廠模式時(shí)噪生,對(duì)產(chǎn)品等級(jí)結(jié)構(gòu)的劃分是非常重要的。

總結(jié)

三種工廠模式都各有優(yōu)缺點(diǎn)东囚,合適的才是最好的跺嗽,希望大家能夠理解并靈活運(yùn)用,讓自己的代碼變得更優(yōu)雅页藻!

END

往期推薦

吳亦凣事件告訴我們桨嫁,不懂中間人攻擊會(huì)吃大虧

就這?Spring 事務(wù)失效場(chǎng)景及解決方案

就這份帐?一篇文章讓你讀懂 Spring 事務(wù)

SpringBoot+Redis 實(shí)現(xiàn)消息訂閱發(fā)布

什么璃吧?你還不會(huì)在GitHub上搜索資源?還不點(diǎn)進(jìn)來(lái)看看废境?

更多精彩推薦畜挨,請(qǐng)關(guān)注公眾號(hào)【靚仔聊編程】

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市噩凹,隨后出現(xiàn)的幾起案子巴元,更是在濱河造成了極大的恐慌,老刑警劉巖驮宴,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件逮刨,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡堵泽,警方通過(guò)查閱死者的電腦和手機(jī)修己,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)迎罗,“玉大人睬愤,你說(shuō)我怎么就攤上這事〖亚” “怎么了戴涝?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我啥刻,道長(zhǎng)奸鸯,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任可帽,我火速辦了婚禮娄涩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘映跟。我一直安慰自己蓄拣,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布努隙。 她就那樣靜靜地躺著球恤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪荸镊。 梳的紋絲不亂的頭發(fā)上咽斧,一...
    開(kāi)封第一講書(shū)人閱讀 49,144評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音躬存,去河邊找鬼张惹。 笑死,一個(gè)胖子當(dāng)著我的面吹牛岭洲,可吹牛的內(nèi)容都是我干的宛逗。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼盾剩,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼雷激!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起彪腔,我...
    開(kāi)封第一講書(shū)人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤侥锦,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后德挣,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體恭垦,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年格嗅,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了番挺。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡屯掖,死狀恐怖玄柏,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情贴铜,我是刑警寧澤粪摘,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布瀑晒,位于F島的核電站,受9級(jí)特大地震影響徘意,放射性物質(zhì)發(fā)生泄漏苔悦。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一椎咧、第九天 我趴在偏房一處隱蔽的房頂上張望玖详。 院中可真熱鬧,春花似錦勤讽、人聲如沸蟋座。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)向臀。三九已至,卻和暖如春莫矗,著一層夾襖步出監(jiān)牢的瞬間飒硅,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工作谚, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人庵芭。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓妹懒,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親双吆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子眨唬,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

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