剛剛學設計模式,不知道理解是否正確,應該有很多不對的地方,請大家指點。
簡單工廠模式
問題背景
大眾集團要生產汽車,但是不確定要生產哪幾個品牌的汽車。
角色扮演
- 車:
VwCar
類、AudiCar
類 - 工廠:
Factory
類 - 客戶端:需要去例項化車的人或者其它程式碼
適用場景
要生產的產品的數目和型別未知的時候。例如你的專案經理今天突然說:"需要實現一個匯出為PDF的的功能"。
實現
車
class VwCar
{
}
class AudiCar
{
}
工廠
class Factory
{
//這兩個常量用於給客戶端一個友好的提示,不要也行
const VW = 1;
const AUDI = 2;
public function produce($type)
{
switch ($type) {
case self::VW:
return new VwCar();
break;
case self::AUDI:
return new AudiCar();
break;
}
}
}
客戶端
$factory = new Factory();
$vw = $factory->produce(Factory::VW); //生產大眾車
var_dump($vw); //輸出 object(VwCar)#3 (0) { }
可以看到,生產了一輛大眾車。這個時候,如果還需要生產保時捷,那麼增加一條產品線,稍微改改工廠即可。
class Factory
{
//這三個常量用於給客戶端一個友好的提示,不要也行
const VW = 1;
const AUDI = 2;
const PORSCHE = 3; // <----增加了一個常量
public function produce($type)
{
switch ($type) {
case self::VW:
return new VwCar();
break;
case self::AUDI:
return new AudiCar();
break;
case self::PORSCHE: // <----增加了一條產品線,用於生產保時捷
return new PorscheCar();
break;
}
}
}
- 優點
- 增加產品的時候,只需要修改
Factory
類,增加相應的邏輯即可。
- 增加產品的時候,只需要修改
- 需要改進的地方
- 在工廠類的內部用了
switch
語句,用於判斷new
什麼物件(生產什麼車),這就是耦合的表現。
- 在工廠類的內部用了
當你發現在你的程式碼中使用了大量的條件語句,不妨考慮使用多型來解決問題。
工廠方法模式
為啥叫工廠方法模式,叫啥名不重要,捋清楚邏輯才是關鍵。
問題背景
基於上文簡單工廠模式中提到的需要改進的地方,得想辦法解決。如果我還要生賓利、布加迪,我不得不去修改工廠方法中的程式碼,但是我並不想這樣做。
角色扮演
- 車:
VwCar
類、AudiCar
類 - 總工廠:
Factory
抽象類,或者介面也行 - 子工廠:
VwCarFactory
類、AudiCarFactory
類 - 客戶端:需要去例項化車的人或者其它程式碼
適用場景
要生產的產品的數目和型別未知的時候。
實現
車
class VwCar
{
}
class AudiCar
{
}
總工廠
abstract class Factory
{
abstract public function produce();
}
子工廠
class VwCarFactory extends Factory
{
public function produce()
{
return new VwCar();
}
}
class AudiCarFactory extends Factory
{
public function produce()
{
return new AudiCar();
}
}
客戶端
$factory = new VwCarFactory(); //例項化大眾車工場
$vw = $factory->produce(); //生產大眾車
var_dump($vw); //輸出 object(VwCar)#3 (0) { }
可以看到,生產了一輛大眾車,不同於簡單工廠的是,這時是使用大眾車的工廠去生產大眾車,煩人的條件語句沒有了。且作為客戶端,我不需要關心 $factory->produce()
的具體實現,或者說不需要關心你這車怎麼生產的,你把車給我就好。
如果還需要生產保時捷,那麼新建一個工廠即可。
class PorscheCarFactory extends Factory
{
public function produce()
{
return new PorscheCar();
}
}
利用多型,消除了煩人的條件語句。
- 優點
- 增加產品的時候,只需要增加對應的工廠類。
- 需要改進的地方
- 只能橫向擴充套件,無法縱向擴充套件。比如說,大眾集團決定對每種車生產低端車和高階車。
讀到這裡,你可能感覺我在扯犢子,我隱約感覺到我確實是在扯犢子,可能扯著扯著就整明白了。
抽象工廠模式
問題背景
為了解決上文提到的,只能"橫向"擴充套件,無法"縱向"擴充套件的問題。這裡引出一個概念,產品族。我並不想針對每一個系列的車都去建一個工廠,畢竟建工廠要很多錢,我希望建一個奧迪車工廠就可以生產低端奧迪和高階奧迪,將來可能還會有中端、頂端等等。
無論是低端奧迪還是高階奧迪,都是奧迪車。
角色扮演
- 低端車介面:
LowEndCar
類(生產低端車就要有低端車的模板,例如廠家規定低端車不端備八向電動座椅調節) - 車:
VwLowEndCar
類(低端大眾) - 總工廠:
Factory
抽象類,或者介面也行 - 子工廠:
VwCarFactory
類 - 客戶端:需要去例項化車的人或者其它程式碼
適用場景
要生產的產品的數目和型別未知的時候。與工廠方法類似,是高階版的"工廠方法"。
實現(基本款)
低端車介面
interface LowEndCar
{
//規定一些低端車必須實現的方法,例如低端車必須不端備八向電動座椅調節
}
大眾低端車
class VwLowEndCar implements LowEndCar
{
}
總工廠
abstract class Factory
{
abstract public function produceLowEndCar();
}
大眾車的子工廠
class VwCarFactory extends Factory
{
public function produceLowEndCar()
{
return new VwLowEndCar();
}
}
客戶端
$factory = new VwCarFactory(); //例項化大眾車工廠
$VwLowEndCar = $factory->produceLowEndCar(); //生產大眾低端車
var_dump($VwLowEndCar); //輸出 object(VwLowEndCar)#3 (0) { }
此時廠家說要生產高階車,沒問題,那麼需要做幾個事情呢?
實現(生產高階車)
第一,給出高階車的介面。
高階車介面
interface HeightEndCar
{
//規定一些高階車必須實現的方法,例如高階車必須端備座椅加熱
}
第二,給出具體的大眾高階車。
大眾高階車
class VwHeightEndCar implements HeightEndCar
{
}
第三,在總工廠中給出生產高階車的方法。
總工廠
abstract class Factory
{
abstract public function produceLowEndCar();
abstract public function produceHeightEndCar();
}
第四,在具體的大眾車工廠中去實現生產高階車的方法。
大眾車的子工廠
class VwCarFactory extends Factory
{
public function produceLowEndCar()
{
return new VwLowEndCar();
}
public function produceHeightEndCar() // <----可以生產高階車了
{
return new VwHeightEndCar();
}
}
第五,客戶端呼叫。
客戶端
$factory = new VwCarFactory(); //例項化大眾車工廠
$VwHeightEndCar = $factory->produceHeightEndCar(); // 生產大眾高階車
var_dump($VwHeightEndCar); //輸出 object(VwHeightEndCar)#3 (0) { }
看到這裡,需要回頭想想,這樣做有啥好處?
這個時候,如果我們要增加產品線,就很輕鬆愉悅了。至少不用去修改總工廠的某些具體方法,雖然本例中總工廠中沒有任何具體的方法,但是完全可以加入一些,例如無論是高階車低端車,總得給出車的長度吧。
abstract class Factory
{
abstract public function produceLowEndCar();
abstract public function produceHeightEndCar();
public function getWidth()
{
return '獲取車的長度';
}
}
這裡的 getWidth()
,無論哪一個具體的子工廠都可以共享,豈不是輕鬆愉悅。
實現(生產奧迪車)
還是要考慮,要做哪些事情?
第一,給出奧迪車。
class AudiLowEndCar implements LowEndCar
{
}
class AudiHeightEndCar implements HeightEndCar
{
}
第二,給出奧迪車的子工廠。
class AudiCarFactory extends Factory
{
public function produceLowEndCar()
{
return new AudiLowEndCar();
}
public function produceHeightEndCar()
{
return new AudiHeightEndCar();
}
}
第三,客戶端呼叫。
$factory = new AudiCarFactory(); //例項化奧迪車工廠
$AudiLowEndCar = $factory->produceLowEndCar(); // 生產奧迪低端車
$AudiHeightEndCar = $factory->produceHeightEndCar(); // 生產奧迪高階車
var_dump($AudiLowEndCar); //輸出 object(AudiLowEndCar)#3 (0) { }
var_dump($AudiHeightEndCar); //輸出 object(AudiHeightEndCar)#4 (0) { }
思考
如果現在廠家要生產中端車,那些地方要動刀子?
- 需要6刀
- 中端車介面
- 中端大眾車去實現中端車介面
- 中端奧迪車去實現中端車介面
- 總工廠增加抽象的建立中端車的方法
- 大眾的子工廠去實現總工廠中建立中端車的方法
- 奧迪的子工廠去實現總工廠中建立中端車的方法