學(xué)好設(shè)計模式防被祭天:工廠模式

工廠模式

為了防止被“殺”了祭天弦聂,學(xué)點設(shè)計模式鸟辅,并總結(jié)下還是有必要的。

一:模式理解

  1. 工廠模式的作用是新建對象横浑。
  2. 工廠模式的目的是讓新建對象的過程更加優(yōu)雅剔桨,減少對業(yè)務(wù)代碼的混淆。
  3. 包含簡單工廠徙融,工廠方法,抽象工廠瑰谜。
  4. 以下幾點可以在例子之后再看欺冀。
  5. 單獨建立用于生成對象的工程類树绩,即為簡單工廠,類似于工具類隐轩。
  6. 在原有代碼上饺饭,抽象出一個方法,用于生成對象职车,即為工廠方法瘫俊。
  7. 新建一個完全抽象的工廠接口/類,具體生成的對象由該工廠的實現(xiàn)類/子類決定悴灵,即為抽象工廠扛芽。


二: 例子

你是一個富二代,擁有有一家汽車公司积瞒,公司的主要工作是把汽車賣給客戶川尖,即sellCar。

主要流程包括原料采購茫孔,組裝汽車叮喳,汽車展示。

為方便模擬缰贝,三個步驟分別為:

  1. 原料采購直接sout原料采購馍悟。
  2. 組裝汽車(assembleCar)生成對應(yīng)的汽車對象。
  3. 汽車展示調(diào)用生成汽車對象的display方法剩晴,確保生產(chǎn)正確锣咒。
富二代家的汽車公司

有一個汽車類,包含屬性為牌子李破,顏色宠哄,還有一個display方法。

// 汽車類
@Data
public class Car {
    private String brand;
    private String color;

    public void display() {
        System.out.println("This is a car");
    }
}

目前你司主營奔馳和寶馬車嗤攻,均繼承自父類Car毛嫉,對應(yīng)的類分別為:

// 寶馬車實體類
public class BMWCar extends Car{
    @Override
    public void display(){
        System.out.println("This is a BMW car");
    }
}

// 奔馳車實體類
public class BenzCar extends Car {
    @Override
    public void display(){
        System.out.println("This is a Benz car");
    }
}

兩個類沒什么區(qū)別,只是分別重寫了Car類的display方法妇菱。

你司的日常就是賣汽車(sellCar)承粤,根據(jù)不同的訂單,輸入不同的carType闯团,新建不同的汽車對象辛臊。

public class CarCompany {
    public void sellCar(String carType) {
        System.out.println("原料采購");
        Car car = null;
        if (StringUtils.equals(carType, "BMW")) {
            car = new BMWCar();
        } else if (StringUtils.equals(carType, "Benz")) {
            car = new BenzCar();
        } else {
            car = new Car();
        }
        car.display();
    }

    public static void main(String[] args) {
        CarCompany carCompany = new CarCompany();
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNext()) {
            String carType = scanner.next();
            carCompany.sellCar(carType);
        }
    }
}

輸入/輸出:

BMW
原料采購
This is a BMW car
Benz
原料采購
This is a Benz car
car
原料采購
This is a car

可以看出,你司的業(yè)務(wù)還是挺簡單粗暴房交,只需判斷輸入生產(chǎn)不同的車即可彻舰。

作為富二代的你并不打算對其做出什么改變。

然而,有一天刃唤,你發(fā)現(xiàn)奧迪車賣的也不錯隔心,準(zhǔn)備開始在自己公司賣奧迪車。

此時在業(yè)務(wù)代碼sellCar中增加一個else判斷條件尚胞。

你的公司越做越大硬霍,每天都會需要決定加入或者刪除某些車是否生產(chǎn)。

某一次笼裳,一個程序員在修改if else的過程中唯卖,不小心搞出個bug。由于習(xí)慣性復(fù)制粘貼躬柬,在應(yīng)該生產(chǎn)QQ車的時候拜轨,生產(chǎn)了一輛賓利車。

為此楔脯,你“殺”了這位程序員祭天撩轰。

有沒有辦法可以在不修改業(yè)務(wù)代碼的情況下,搞定第二步驟昧廷,即組裝汽車邏輯的修改呢堪嫂?

1. 簡單工廠

將新建對象的邏輯從業(yè)務(wù)代碼中提取出來。

新建一個簡單汽車工廠類來承載新建汽車對象的功能木柬。

public class SimpleCarFactory {
    public static Car assembleCar(String carType) {
        Car car = null;
        if (StringUtils.equals(carType, "BMW")) {
            car = new BMWCar();
        } else if (StringUtils.equals(carType, "Benz")) {
            car = new BenzCar();
        } else {
            car = new Car();
        }
        return car;
    }
}

修改之后皆串,汽車工廠的代碼變?yōu)椋?/p>

public class CarCompany {
    public void sellCar(String carType) {
        // 原料采購
        System.out.println("原料采購");
        // 組裝汽車
        Car car = SimpleCarFactory.assembleCar(carType);
        // 展示汽車
        car.display();
    }

    public static void main(String[] args) {
        CarCompany carCompany = new CarCompany();
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNext()) {
            String carType = scanner.next();
            carCompany.sellCar(carType);
        }
    }
}

輸入和輸出都與之前一樣。

  1. 嚴(yán)格意義上眉枕,簡單工廠并不是一種設(shè)計模式恶复,更像是一種重構(gòu)。
  2. 重構(gòu)之后速挑,CarCompany的sellCar代碼變得非常清晰谤牡,特別是引入方法assembleCar后,一看就可以知道該步驟是在組裝汽車姥宝。
  3. 對于剛看代碼的新手而言翅萤,就算不理解assembleCar具體操作,也可以根據(jù)該方法名知道該方法的作用腊满。
  4. 經(jīng)過程序員的一頓改套么,你特別高興,自己家又多了一個工廠碳蛋,又多了個廠長的身份胚泌,還高興地freestyle了一番。

2. 方法工廠

工廠的目的是生成一個對象肃弟,簡單工廠為此新建了一個類玷室。

此外零蓉,也可以在CarCompany類下增加一個新建汽車對象的私有方法。

該方法的作用和簡單工廠一致阵苇,不同的是以方法的形式出現(xiàn)壁公,將其稱為方法工廠感论。

public class CarCompany {
    public void sellCar(String carType) {
        System.out.println("原料采購");
        Car car = assembleCar(carType);
        car.display();
    }

    private Car assembleCar(String carType) {
        Car car = null;
        if (StringUtils.equals(carType, "BMW")) {
            car = new BMWCar();
        } else if (StringUtils.equals(carType, "Benz")) {
            car = new BenzCar();
        } else {
            car = new Car();
        }
        return car;
    }

    public static void main(String[] args) {
        CarCompany carCompany = new CarCompany();
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNext()) {
            String carType = scanner.next();
            carCompany.sellCar(carType);
        }
    }
}

當(dāng)然绅项,如果只是將方法的實現(xiàn)移動一個位置,那也算不上是設(shè)計模式比肄。

好不容易多了個廠長的身份快耿,程序員竟然這么改。你頓時不高興了芳绩,回頭就準(zhǔn)備掏出自己40米的大刀掀亥。

此時,程序員急忙開始解釋妥色。

如果將CarCompany中的assembleCar方法申明為抽象方法搪花,具體實現(xiàn)由子類來決定。

public abstract class CarCompany {
    public void sellCar(String carType) {
        System.out.println("原料采購");
        Car car = assembleCar(carType);
        car.display();
    }

    protected abstract Car assembleCar(String carType);
}

此時嘹害,你完全可以開多家公司用于生產(chǎn)不同的車撮竿,它們都將繼承自CarCompany。

CarCompany類規(guī)定了sellCar方法的步驟笔呀,留下組裝汽車的方法讓不同的公司自己去實現(xiàn)幢踏。

新建兩個公司,一個賣寶馬许师,一個賣奔馳房蝉,分開之后,業(yè)務(wù)得到了擴展微渠,兩個工廠都可以生產(chǎn)轎車和SUV搭幻。

public class BMVCarCompany extends CarCompany {
    @Override
    protected Car assembleCar(String carType) {
        Car car = null;
        if (StringUtils.equals(carType, "car")) {
            car = new BMWCar();
        } else if (StringUtils.equals(carType, "suv")) {
            car = new BMWSUVCar();
        } else {
            car = new Car();
        }
        return car;
    }
}
public class BenzCarCompany extends CarCompany {
    @Override
    protected Car assembleCar(String carType) {
        Car car = null;
        if (StringUtils.equals(carType, "car")) {
            car = new BenzCar();
        } else if (StringUtils.equals(carType, "suv")) {
            car = new BMWSUVCar();
        } else {
            car = new Car();
        }
        return car;
    }
}
public class Client {
    public static void main(String[] args) {
        CarCompany bmwCarCompany = new BMVCarCompany();
        CarCompany benzCarCompany = new BenzCarCompany();
        bmwCarCompany.sellCar("car");
        benzCarCompany.sellCar("car");
        bmwCarCompany.sellCar("suv");
        benzCarCompany.sellCar("suv");
    }
}

輸入/輸出:

原料采購
This is a BMW car
原料采購
This is a Benz car
原料采購
This is a BMW SUV car
原料采購
This is a BMW SUV car

兩個公司可以完全獨立運行,在需要新建一個公司的時候逞盆,只需要增加新的公司類即可檀蹋,如AudiCarCompany。

聽了程序員這番解釋纳击,你雖然沒聽懂续扔,但覺得可以一下子多搞幾個分公司,內(nèi)心還是有點小激動的焕数,隨手收回了自己那把40米的大刀纱昧。

3. 抽象工廠

由于成本變動,寶馬和奔馳子公司都需要更換組裝汽車的過程堡赔,第一時間想到是直接修改兩個公司的assembleCar的代碼识脆。

然而現(xiàn)在寶馬公司又需要生產(chǎn)紀(jì)念版的寶馬車,和普通寶馬不同的是,特別版的寶馬使用了特殊的紅色油漆灼捂。

面對這樣的需求离例,雖貴為富二代的你,還是準(zhǔn)備討好下程序員悉稠,以一起去徹夜鼓掌作為誘惑宫蛆,希望程序員盡快完成。

在之前的步驟中的猛,已經(jīng)將組裝汽車的過程抽象成工廠耀盗,那么更改/修改汽車組裝的過程,可以抽象成換了一家代工廠卦尊。

但無論怎么換代工廠叛拷,都要求這些工廠有assembleCar的功能,所以需要一個抽象類或者接口來約束岂却。其中的組裝方法是抽象的忿薇,具體實現(xiàn)在子類中定義。

這就是抽象工廠這個名稱的含義躏哩。

public abstract class AbstractCarFactory {
    public abstract Car assembleCar(String carType);
}

分別新建兩家代工廠署浩。

// 寶馬車代工廠
public class BMWCarFactory extends AbstractCarFactory {
    @Override
    public Car assembleCar(String carType) {
        Car car = null;
        if (StringUtils.equals(carType, "car")) {
            car = new BMWCar();
        } else if (StringUtils.equals(carType, "suv")) {
            car = new BMWSUVCar();
        } else {
            car = new Car();
        }
        return car;
    }
}

// 奔馳車代工廠
public class BenzCarFactory extends AbstractCarFactory {
    @Override
    public Car assembleCar(String carType) {
        Car car = null;
        if (StringUtils.equals(carType, "car")) {
            car = new BenzCar();
        } else if (StringUtils.equals(carType, "suv")) {
            car = new BMWSUVCar();
        } else {
            car = new Car();
        }
        return car;
    }
}

兩家公司分別持有AbstractCarFactory對象,在assembleCar方法中震庭,只需直接返回對應(yīng)工廠組裝的汽車即可瑰抵,不用關(guān)注具體的工藝。

public class BMVCarCompany extends CarCompany {
    private AbstractCarFactory carFactory;

    public BMVCarCompany(AbstractCarFactory carFactory) {
        this.carFactory = carFactory;
    }

    @Override
    protected Car assembleCar(String carType) {
        return carFactory.assembleCar(carType);
    }
}
public class BenzCarCompany extends CarCompany {
    private AbstractCarFactory carFactory;

    public BenzCarCompany(AbstractCarFactory carFactory) {
        this.carFactory = carFactory;
    }

    @Override
    protected Car assembleCar(String carType) {
        return carFactory.assembleCar(carType);
    }
}
public class Client {
    public static void main(String[] args) {
        AbstractCarFactory bmwCarFactory = new BMWCarFactory();
        CarCompany bmwCarCompany = new BMVCarCompany(bmwCarFactory);
        AbstractCarFactory benzCarFactory = new BenzCarFactory();
        CarCompany benzCarCompany = new BenzCarCompany(benzCarFactory);
        bmwCarCompany.sellCar("car");
        benzCarCompany.sellCar("car");
        bmwCarCompany.sellCar("suv");
        benzCarCompany.sellCar("suv");
    }
}

當(dāng)某個工廠需要更換代工廠時候器联,只需新建一個繼承自AbstractCarFactory抽象工廠的具體工廠即可二汛,如AudiCarFactory。

并將此工廠作為carCompany的屬性置入拨拓,如果只希望增加代碼而不是修改代碼肴颊,可以新建AudiCarCompany即可。

以下例子為寶馬公司更換特別版生產(chǎn)工廠之后渣磷,生產(chǎn)寶馬車的過程婿着,在display中同時展示寶馬車的顏色。

可以看到醋界,在更換特別版工廠之后竟宋,生產(chǎn)出來的寶馬車加上了特別的紅色。

而作為富二代的你形纺,開上這輛特別版的寶馬丘侠,突然發(fā)現(xiàn)一下子多了好多一夜成長的機會。

此時逐样,你已經(jīng)忘了答應(yīng)給程序員的鼓掌獎勵蜗字。/(ㄒoㄒ)/~~

public class BMWCarFactoryForSpecialEdition extends AbstractCarFactory {
    @Override
    public Car assembleCar(String carType) {
        Car car = null;
        if (StringUtils.equals(carType, "car")) {
            car = new BMWCar();
        } else if (StringUtils.equals(carType, "suv")) {
            car = new BMWSUVCar();
        } else {
            car = new Car();
        }
        car.setColor("Red for special edition");
        return car;
    }
}
public class BMWCar extends Car {
    @Override
    public void display() {
        System.out.println("This is a BMW car with " + getColor());
    }
}

輸入/輸出:

原料采購
This is a BMW car with Red for special edition


三: 再理解

  1. 簡單工廠只是一個代碼重構(gòu)的過程打肝,為組裝汽車的過程新建了一個類,一個靜態(tài)方法挪捕。
  2. 方法工廠可以分為簡單方法工廠和抽象方法工廠粗梭。簡單方法工廠和簡單方法差不多,只是少新建了一個工廠類级零。抽象方法工廠断医,將生成對象的方法申明為抽象,在子類中實現(xiàn)妄讯,方便建立新的公司孩锡,業(yè)務(wù)拆分。
  3. 抽象工廠亥贸,定義一個什么都不做,只約定做什么的工廠浇垦,即將工廠的作用抽象出來炕置。抽象工廠和策略模式很類似,只是抽象工廠的目的是生成對象男韧,而策略模式的目的主要在于為不同類別的對象執(zhí)行不同的步驟朴摊。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市此虑,隨后出現(xiàn)的幾起案子甚纲,更是在濱河造成了極大的恐慌,老刑警劉巖朦前,帶你破解...
    沈念sama閱讀 217,907評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件介杆,死亡現(xiàn)場離奇詭異,居然都是意外死亡韭寸,警方通過查閱死者的電腦和手機春哨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來恩伺,“玉大人赴背,你說我怎么就攤上這事【” “怎么了凰荚?”我有些...
    開封第一講書人閱讀 164,298評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長褒脯。 經(jīng)常有香客問我便瑟,道長,這世上最難降的妖魔是什么憨颠? 我笑而不...
    開封第一講書人閱讀 58,586評論 1 293
  • 正文 為了忘掉前任胳徽,我火速辦了婚禮积锅,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘养盗。我一直安慰自己缚陷,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,633評論 6 392
  • 文/花漫 我一把揭開白布往核。 她就那樣靜靜地躺著箫爷,像睡著了一般。 火紅的嫁衣襯著肌膚如雪聂儒。 梳的紋絲不亂的頭發(fā)上虎锚,一...
    開封第一講書人閱讀 51,488評論 1 302
  • 那天,我揣著相機與錄音衩婚,去河邊找鬼窜护。 笑死,一個胖子當(dāng)著我的面吹牛非春,可吹牛的內(nèi)容都是我干的柱徙。 我是一名探鬼主播,決...
    沈念sama閱讀 40,275評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼奇昙,長吁一口氣:“原來是場噩夢啊……” “哼护侮!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起储耐,我...
    開封第一講書人閱讀 39,176評論 0 276
  • 序言:老撾萬榮一對情侶失蹤羊初,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后什湘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體长赞,經(jīng)...
    沈念sama閱讀 45,619評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,819評論 3 336
  • 正文 我和宋清朗相戀三年禽炬,在試婚紗的時候發(fā)現(xiàn)自己被綠了涧卵。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,932評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡腹尖,死狀恐怖柳恐,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情热幔,我是刑警寧澤乐设,帶...
    沈念sama閱讀 35,655評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站绎巨,受9級特大地震影響近尚,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜场勤,卻給世界環(huán)境...
    茶點故事閱讀 41,265評論 3 329
  • 文/蒙蒙 一戈锻、第九天 我趴在偏房一處隱蔽的房頂上張望歼跟。 院中可真熱鬧,春花似錦格遭、人聲如沸哈街。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽骚秦。三九已至,卻和暖如春璧微,著一層夾襖步出監(jiān)牢的瞬間作箍,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評論 1 269
  • 我被黑心中介騙來泰國打工前硫, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留胞得,地道東北人。 一個月前我還...
    沈念sama閱讀 48,095評論 3 370
  • 正文 我出身青樓开瞭,卻偏偏與公主長得像懒震,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子嗤详,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,884評論 2 354

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