前言
以小說的筆法寫的設計模式系列文章,你絕對看得懂![首發於公眾號:"聊聊程式碼"]
設計模式系列·王小二需求歷險記(一)
設計模式系列·王小二需求歷險記(二)
設計模式系列·封裝、繼承、多型
設計模式系列·初探設計模式之王小二的疑問
設計模式系列·Facade模式之MVC的煩惱
設計模式系列·Adapter 模式之如何優雅的使用別人的輪子
設計模式系列·類爆炸之Bridge模式
設計模式系列·工廠方法模式之 Code Review
設計模式系列·抽象工廠模式
------華麗的分割線:正文開始------
午後閒談
公司最近專案不忙,午間小憩之後,小二找到C哥攀談了起來。
"C哥,忙啥呢?"
"也沒忙啥,就是隨便看看。"
"哦哦,我最近也不怎麼忙。你上次給我講的工廠模式,受益匪淺啊!"
"哈哈,是嘛!其實你不知道,還有抽象工廠模式呢!"
"抽象工廠模式?願聞其詳。"
"好,反正最近也不忙,就給你講講吧。"
從奧迪車說起
"小二,你知道,奧迪A4與A6,他們使用的輪胎與燈泡是不一樣的。"
"是,型號不一樣,輪胎與燈泡肯定不一樣。"
"假設現在需要製造A4與A6的輪胎和燈泡,你會怎麼寫程式碼?"
"這個嘛,好寫!"
小二熟練的開啟電腦,揮斥方遒,迅速的寫出了程式碼。
<?php
class Client{
//根據不同型號生產不同輪胎
public function produce_wheel($type){
switch ($type){
case 'A4':
$obj=new AudiA4Wheel();
break;
case 'A6':
$obj=new AudiA6Wheel();
break;
default:
throw new Exception('no instance found');
}
$obj->produce_wheel();
}
//根據不同型號生產不同燈泡
public function produce_light($type){
switch ($type){
case 'A4':
$obj=new AudiA4Light();
break;
case 'A6':
$obj=new AudiA6Light();
break;
default:
throw new Exception('no instance found');
}
$obj->produce_light();
}
}
$client=new Client();
$client->produce_wheel('A4');
$client->produce_light('A4');複製程式碼
"C哥,大體就是這麼個思路。您看看對嗎?"
"嗯,這程式碼確實也實現了功能,但是,有問題。"
"有問題?什麼問題?"
"你這程式碼存在著低內聚、高耦合的問題,不好維護啊。"
"怎麼低內聚、高耦合了?"小二一臉茫然。
C哥不慌不忙的解釋道:
比如,現在我要增加奧迪的型號A8,那你程式碼裡的函式produce_wheel()
與produce_light()
是不是都要改?也就是這兩個函式是相互依賴的,不可能用A6的輪子,而用A8的燈泡。相互依賴,是為高耦合。
produce_wheel()
與 produce_light()
函式,這兩個函式都關心自己需要什麼型號的產品,並且都負責把相應的產品生產出來。也就是,他有兩個職責:關心型號、根據不同型號生產出對應的產品。但這兩個職責是毫無關聯的,沒有半毛錢的關係。職責過多且分散,是為低內聚。
"C哥這麼一說,還真是這麼回事。"
"哈哈,低內聚高耦合的程式碼也能實現需求,但是這樣的程式碼不好維護。"
"嗯嗯,C哥,有啥好辦法嗎?"
"當然有了,我們這裡就用到了抽象工廠模式。"
抽象工廠模式現身
"根據我多年的經驗,出現switch語句的地方,往往意味著需要抽象、或者存在著放錯責任的地方。"
"寶貴的經驗,記下了!"
"小二,其實這裡,就是放錯了責任。"
"嗯,怎麼說呢?"
"Client端既關心如何建立物件,又關心如何用物件來製造輪子、燈泡。"
"是,他的責任太多了。"
"其實,Client端只負責使用物件製造相關產品就行了。他不用負責創造物件。創造物件,交給Client端來做,就是放錯了責任。"
"對,確實是這樣。"
"還記得前幾天給你講的工廠模式嗎?工廠模式也是為了解決這個問題。"
"記得記得,工廠模式也是為了實現責任的分離。"
"工廠模式針對一種產品提供一個工廠類,而抽象工廠模式是針對一組相關或相互依賴的產品提供一個工廠類。"
"那抽象工廠模式就是工廠模式的升級版本啦!"
"是的。在這裡,Client端負責向Factory發出請求,Factory返回相關物件,Client端再根據Factory返回的物件,製造相關的產品。"
"也就是Client負責使用物件,Factory負責建立物件!"
"是的,小二很聰明嘛!看看抽象工廠模式的類圖吧!"
"好的,C哥。"
用抽象工廠模式來解決問題
"小二啊,跟你講了這麼多,接下來就看你了!"
"好的C哥,我馬上畫出類圖、寫出程式碼。"
小二仿照著C哥的類圖,又畫出了用抽象工廠解決上面問題的類圖。
畫好類圖,程式碼也就好寫了!
<?php
//燈泡產品介面
interface Light{
public function produce_light();
}
//奧迪A4燈泡產品
class AudiA4Light implements Light{
public function produce_light()
{
echo "AudiA4 Light produced!\n";
}
}
//奧迪A6燈泡產品
class AudiA6Light implements Light{
public function produce_light()
{
echo "AudiA6 Light produced!\n";
}
}
//輪子產品介面
interface Wheel{
public function produce_wheel();
}
//奧迪A4輪子
class AudiA4Wheel implements Wheel {
public function produce_wheel()
{
echo "AudiA4 Wheel produced!\n";
}
}
//奧迪A6輪子
class AudiA6Wheel implements Wheel {
public function produce_wheel()
{
echo "AudiA6 Wheel produced!\n";
}
}
//工廠介面
interface Factory{
public function CreateWheel();
public function CreateLight();
}
//奧迪A4工廠
class A4Factory implements Factory {
public function CreateWheel()
{
return new AudiA4Wheel();
}
public function CreateLight()
{
return new AudiA4Light();
}
}
//奧迪A6工廠
class A6Factory implements Factory {
public function CreateWheel()
{
return new AudiA6Wheel();
}
public function CreateLight()
{
return new AudiA6Light();
}
}
//客戶端呼叫類
class Client{
//執行主函式
public static function main($type){
$reflection=new ReflectionClass($type.'Factory');
$factory=$reflection->newInstance();
self::run($factory);
}
//生產產品
public static function run(Factory $factory){
$wheel=$factory->CreateWheel();
$wheel->produce_wheel();
$light=$factory->CreateLight();
$light->produce_light();
}
}
Client::main('A6');複製程式碼
斬獲新技能
"嗯嗯,小二不錯嘛。簡單工廠、工廠方法、抽象工廠模式,你都掌握了。"
"哈哈,感謝C哥的教導!"
"恭喜你在設計模式打怪升級的道路上,再次斬獲新技能!"
聽到這句話,小二心裡美滋滋的,嘴角露出了得意的微笑......
轉載宣告:本文轉載自「聊聊程式碼」,搜尋「talkpoem」即可關注。
關注「聊聊程式碼」,讓我們一起聊聊“左手程式碼右手詩”的事兒。