前言
隨著編程技術(shù)的不斷發(fā)展周蹭,面向?qū)ο笳Z言和面向?qū)ο蟪绦蛟O(shè)計(jì)逐漸成為主流绰筛。這就不可避免地涉及到了對象的創(chuàng)建。創(chuàng)建一個(gè)對象逐工,并使用已經(jīng)定義好的方法铡溪,看起來也很清晰和簡單。有的時(shí)候泪喊,在不同的情況下需要不同子類的對象棕硫,如何降低耦合度、方便地進(jìn)行切換袒啼,而不需要將所有實(shí)例化該對象的地方都進(jìn)行修改哈扮,則涉及到了模式纬纪。
下面將依次介紹簡單工廠模式、工廠方法模式滑肉、抽象工廠模式包各,說明他們是如何實(shí)現(xiàn)創(chuàng)建對象這一功能的。(后文將三者統(tǒng)稱為工廠模式靶庙。)
簡單工廠模式
一個(gè)很簡單的做法问畅,既然你說可能要使用不同的對象,那我將所有創(chuàng)建對象的操作都集中起來六荒,只在一個(gè)地方進(jìn)行對象的創(chuàng)建护姆,其他地方都通過這里來創(chuàng)建對象。以后即便要修改掏击,也只需要改一處地方不就可以了卵皂。
以生產(chǎn)鼠標(biāo)為例,我們需要有線鼠標(biāo)和無線鼠標(biāo)砚亭。則大致的代碼如下:
abstract class Mouse {
}
class WiredMouse : Mouse {
}
class WirelessMouse : Mouse {
}
class SimpleFactory_1 {
public Mouse createMouse(String type) {
if ("wired".equals(type)) {
return new WiredMouse();
} else if ("wireless".equals(type)) {
return new WirelessMouse();
} else {
// unknown type !
return null;
}
}
}
簡單工廠模式十分簡單直觀灯变,代碼量也很少。這份代碼的一個(gè)改進(jìn)之處是將 createMouse 的參數(shù)從 String 類型改成枚舉類型捅膘,以免使用時(shí)拼寫錯(cuò)誤柒凉,也能更好地對參數(shù)進(jìn)行限制。
簡單工廠模式的缺點(diǎn)是篓跛,如果我們需要增加新的鼠標(biāo)膝捞,那么我們就必須修改 createMouse 方法,增加 if/else 語句愧沟。這樣就破壞了"對修改封閉"的原則蔬咬。
簡單工廠模式可以定義成靜態(tài)方法,也可以定義成實(shí)例方法沐寺。他們的特點(diǎn)如下:
- 靜態(tài)方法
- 無需創(chuàng)建對象就能使用
- 不能使用繼承來改變創(chuàng)建方法的行為
- 實(shí)例方法
- 需要?jiǎng)?chuàng)建對象才能使用
- 可以使用繼承來改變創(chuàng)建方法的行為
更進(jìn)一步林艘,假設(shè)有 A、B 兩家公司都生產(chǎn)有線和無線鼠標(biāo)混坞,他們的鼠標(biāo)定義如下:
// A 的鼠標(biāo)
class AWiredMouse : WiredMouse {
}
class AWirelessMouse : WirelessMouse {
}
// B 的鼠標(biāo)
class BWiredMouse : WiredMouse {
}
class BWirelessMouse : WirelessMouse {
}
對應(yīng)的簡單工廠模式代碼如下:
class SimpleFactory_2 {
public Mouse createMouse(String factory, String type) {
if ("A".equeals(factory)) {
if ("wired".equals(type)) {
return new AWiredMouse();
} else if ("wireless".equals(type)) {
return new AWirelessMouse();
} else {
// unknown type !
return null;
}
} else if ("B".equals(factory)) {
if ("wired".equals(type)) {
return new BWiredMouse();
} else if ("wireless".equals(type)) {
return new BWirelessMouse();
} else {
// unknown type !
return null;
}
} else {
// unknown factory !
return null;
}
}
}
可以看到代碼開始復(fù)雜起來了狐援,如果還有更多公司 C、D 也在生產(chǎn)鼠標(biāo)究孕,或者有更多的鼠標(biāo)類型啥酱,那么這個(gè)工廠類將會(huì)更將復(fù)雜。
工廠方法模式
下面我們就來看看工廠方法模式是如何處理這種問題厨诸。
abstract class Factory {
public Mouse CreateMouse(String type);
}
class AFactory_2 : Factory {
public Mouse CreateMouse(String type) {
if ("wired".equals(type)) {
return new AWiredMouse();
} else if ("wireless".equals(type)) {
return new AWirelessMouse();
} else {
// unknown type !
return null;
}
}
}
class BFactory_2 : Factory {
public Mouse CreateMouse(String type) {
if ("wired".equals(type)) {
return new BWiredMouse();
} else if ("wireless".equals(type)) {
return new BWirelessMouse();
} else {
// unknown type !
return null;
}
}
}
可以看到镶殷,工廠方法模式中,定義了多個(gè)工廠類分別對應(yīng)各自的公司微酬,每個(gè)類只負(fù)責(zé)創(chuàng)建自己的鼠標(biāo)绘趋,而不用管其他公司的颤陶。
如果還有更多的公司,則定義新的工廠類即可陷遮,不需要修改已有的代碼滓走。
如果需要生產(chǎn)更多類型的鼠標(biāo),則需要對工廠都做修改帽馋,增加相應(yīng)的 if/else 語句來處理闲坎。這一點(diǎn)與簡單工廠模式類似。
工廠方法模式茬斧,體現(xiàn)了"對擴(kuò)展開發(fā),對修改封閉"梗逮。
這里為了說明使用工廠方法模式相比簡單工廠模式的優(yōu)點(diǎn)项秉,特意給鼠標(biāo)增加了公司(A、B)和類型(有線慷彤、無線)這兩個(gè)緯度娄蔼,以便說明工廠方法模式對代碼職責(zé)進(jìn)行劃分。
實(shí)際上底哗,對應(yīng)簡單工廠 SimpleFactory_1 也可以改成工廠方法模式岁诉,代碼如下:
abstract class Factory {
public Mouse CreateMouse();
}
class WiredFactory_1 : Factory {
public Mouse CreateMouse() {
return new AWiredMouse();
}
}
class WirelessFactory_1 : Factory {
public Mouse CreateMouse() {
return new AWirelessMouse();
}
}
抽象工廠模式
這些公司除了生產(chǎn)鼠標(biāo)外,還會(huì)生產(chǎn)鍵盤等產(chǎn)品跋选。鍵盤的定義如下:
abstract class Keyboard {
}
class WiredKeyboard : Keyboard {
}
class WirelessKeyboard : Keyboard {
}
// A
class AWiredKeyboard : WiredKeyboard {
}
class AWirelessKeyboard : WirelessKeyboard {
}
// B
class BWiredKeyboard : WiredKeyboard {
}
class BWirelessKeyboard : WirelessKeyboard {
}
對應(yīng)的抽象工廠模式如下:
abstract class Factory {
public Mouse createMouse(String type);
public Keyboard createKeyboard(String type);
}
class AFactory_2 : Factory {
public Mouse createMouse(String type) {
// 與之前一致
}
public Keyboard createKeyboard(String type) {
if ("wired".equals(type)) {
return new AWiredKeyboard();
} else if ("wireless".equals(type)) {
return new AWirelessKeyboard();
} else {
// unknown type !
return null;
}
}
}
class BFactory_2 : Factory {
public Mouse createMouse(String type) {
// 與之前一致
}
public Keyboard createKeyboard(String type) {
if ("wired".equals(type)) {
return new BWiredKeyboard();
} else if ("wireless".equals(type)) {
return new BWirelessKeyboard();
} else {
// unknown type !
return null;
}
}
}
可以看到:現(xiàn)在一個(gè)工廠會(huì)負(fù)責(zé)生產(chǎn)鼠標(biāo)涕癣、鍵盤等多種產(chǎn)品,并且每個(gè)工廠只負(fù)責(zé)自己的部分前标。
如果還有新的公司也可以生成鼠標(biāo)坠韩、鍵盤這些產(chǎn)品,則只需要添加新的工廠類即可炼列,無需修改已有的類只搁。
如果還要生產(chǎn)更多的產(chǎn)品比如顯示器等,則需要修改所有的工廠類俭尖。
這里的例子氢惋,工廠方法也可以拆成抽象工廠來表示。稽犁。
總結(jié)
簡單工廠模式的重點(diǎn)在于將創(chuàng)建產(chǎn)品的代碼統(tǒng)一在一處焰望,方便管理。
工廠方法模式已亥、抽象工廠模式除了將創(chuàng)建產(chǎn)品的代碼統(tǒng)一在一處柿估,還提供了一種便捷地更換產(chǎn)品系列的能力——當(dāng)我需要 A 公司的產(chǎn)品,那么就使用 AAFactory陷猫;當(dāng)我需要 B 公司的產(chǎn)品秫舌,那么就使用 BFactory的妖;只需要修改使用的工廠即可實(shí)現(xiàn)將所有的產(chǎn)品都改成對應(yīng)的產(chǎn)品,大大降低了耦合度和修改的成本足陨。
很多時(shí)候嫂粟,我們只需要用到簡單工廠模式就足夠了。但這并不是工廠方法模式墨缘、抽象工廠模式?jīng)]有用星虹,而是很多時(shí)候沒有這種需求——我們只需要一種類型的產(chǎn)品就足夠了,并不需要同時(shí)支持多種產(chǎn)品镊讼;在這種情況下宽涌,使用簡單工廠模式的確會(huì)更加簡單。
然而蝶棋,一旦我們同時(shí)需要不同類型的產(chǎn)品卸亮,那么簡單工廠模式就很容易使得方法體快速膨脹,使用工廠方法模式和抽象工廠模式可以更好地對代碼和職責(zé)進(jìn)行劃分玩裙。
工廠方法模式只生產(chǎn)一種產(chǎn)品兼贸,而抽象工廠模式則是生產(chǎn)一系列產(chǎn)品(,通常這些產(chǎn)品之間有一定聯(lián)系的)吃溅。抽象工廠模式生產(chǎn)其中的每個(gè)產(chǎn)品時(shí)溶诞,通常使用工廠方法模式。
參考
《Head First 設(shè)計(jì)模式》
《設(shè)計(jì)模式 可復(fù)用面向?qū)ο筌浖幕A(chǔ)》