設計模式系列·抽象工廠模式

子路發表於2017-05-01

前言

以小說的筆法寫的設計模式系列文章,你絕對看得懂![首發於公眾號:"聊聊程式碼"]

設計模式系列·王小二需求歷險記(一)
設計模式系列·王小二需求歷險記(二)
設計模式系列·封裝、繼承、多型
設計模式系列·初探設計模式之王小二的疑問
設計模式系列·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」即可關注。

關注「聊聊程式碼」,讓我們一起聊聊“左手程式碼右手詩”的事兒。

設計模式系列·抽象工廠模式

相關文章