PHP設計模式-幾種工廠模式的對比

工廠模式

在講解工廠模式之前,我們先來探討一些問題谢翎,研究是為什么會出現(xiàn)工廠模式的,工廠模式有什么優(yōu)缺點沐旨。

以超人為例子:有一個超人森逮,超人一定有對種超能力;
于是我們建立一個超人類

namespace Factory;


class Superman
{
    
}

同時創(chuàng)建超能力類并給超人添加超能力磁携;

namespace Factory;


class Flight
{
    /**
     * 能力的名稱
     *
     * @var
     */
    protected $name;

    /**
     * 能力值
     *
     * @var
     */
    protected $ability;

    public function __construct($name, $ability)
    {
        $this->name    = $name;
        $this->ability = $ability;
    }
}

class Force 
{
    //該類的屬性和方法
}

class Jog
{
    //該類的屬性和方法
}

給超人添加超能力

namespace Factory;


class Superman
{
    public function __construct()
    {
        $power_class = new Power('超能力', 1000);
        $force_class = new Force();
        $jog_class   = new Jog();
        
    }
   
}

通過上面的代碼可以得到:

  • 超人要添加一個超能力就必須new個能力類
  • 如果能力類的參數(shù)需要修改褒侧,那么實例化的參數(shù)要需要修改
  • 能力類的名稱覺得不合理,那么調(diào)用的代碼也要修改

如果上面的修改出現(xiàn)在多個不同的地方谊迄,那么我們就會花費很多時間或精力去修改這個東西闷供。所以我們可以引出工廠模式,去解決這些問題统诺。

簡單工廠模式

問題1:
  • 前提: 在超人的超能力不多的情況下(只有一兩個已知的)
  • 懶: 不想在每次添加添加超能力的時候歪脏,寫new
  • 超能力(服務端)不想讓超人(client)知道有哪些超能力類--面向接口
解決方案:簡單工廠模式
  • 首先創(chuàng)建工廠
namespace Factory;


class SimpleFactory
{
    protected $power_type = [
        'force' => __NAMESPACE__ . '\Force',
    ];

    /**
     * 添加超能力
     * @param string $type 超能力類型key
     * @return 超能力的實例化對象
     * @throws \Exception
     */
    public function addPower($type)
    {
        if (!array_key_exists($type, $this->power_type)) {
            throw new \Exception("超能力不存在");
        }

        $class_name = $this->power_type[$type];

        return new $class_name;
    }
}
  • 超能力
namespace Factory;

/**
 * 具體超能力類
 * Class Force
 * @package Factory
 */
class Force extends SimpleFactory
{
    public function setPower()
    {
        echo "超能力:力量" . PHP_EOL;
    }
}
  • 用法
<?php

spl_autoload_register(function($class_name) {
    $class_file = realpath(dirname(__FILE__)."/../") .'/' . str_replace('\\','/', $class_name) . '.php';
    if (file_exists($class_file)) {
        include $class_file;
    }
});

try {
    (new SimpleFactory())->addPower('force')->setPower();
} catch (Exception $e) {
    echo $e->getMessage();
}

總結
  • 使用前提:當工廠知道所有要生產(chǎn)的類型(子類比較少),可以使用簡單工廠模式粮呢。
  • 優(yōu)點:可以使用戶根據(jù)獲得對應類類型婿失,避免了直接實例化類,降低耦合度(面向接口開發(fā):客戶端不用知道服務的有哪些類啄寡,只要知道類型就可以了)豪硅;
  • 缺點:可實例化的類型在編輯期間已經(jīng)被確定,如果增加新類型挺物,則需要修改工廠懒浮,不符合開閉原則。

靜態(tài)工廠模式

問題2: 如果不想在調(diào)用的時候new容器
解決:使用靜態(tài)工廠模式
  • 使用該模式現(xiàn)在我們只要對工廠類的代碼修改一下
namespace Factory;


class SimpleFactory
{
    protected $power_type = [
        'force' => __NAMESPACE__ . '\Force',
    ];

    /**
     * 添加超能力(將方法改成靜態(tài))
     * @param string $type 超能力類型key
     * @return 超能力的實例化對象
     * @throws \Exception
     */
    public static function addPower($type)
    {
        if (!array_key_exists($type, self::power_type)) {
            throw new \Exception("超能力不存在");
        }

        $class_name = self::power_type[$type];

        return new $class_name;
    }
}

總結

與簡單工廠類似识藤,該模式用于創(chuàng)建一組相關或依賴的對象砚著,不同之處在于靜態(tài)工廠模式使用一個靜態(tài)方法來創(chuàng)建所有類型的對象次伶,該靜態(tài)方法通常是 factory 或 build。


因為上面的超能力只有一個稽穆,所以不會出現(xiàn)調(diào)用超能力方法不一樣的情況冠王;如果現(xiàn)在超能力有多個的化,我們最后規(guī)范一下超能力調(diào)用方法接口秧骑。因此引入超能力的接口類。(也可以理解成面向接口開發(fā)扣囊,這樣別人就能夠更加方便調(diào)用)

  • 增加能力接口類
namespace Factory;


/**
* 超能力接口
* Interface PowerInterface
 * @package Factory
*/
interface PowerInterface
{
    public function setPower();
}
  • 對超能力類改造并增加一個超能力類
namespace Factory;


class Force implements PowerInterface
{
    public function setPower()
    {
        echo "超能力:力量" . PHP_EOL;
    }
}
namespace Factory;


class Jog implements PowerInterface
{
    public function setPower()
    {
       echo "超能力:跑步";
    }
}
  • 對簡單工廠類優(yōu)化一下
namespace Factory;


class SimpleFactory
{
    protected $module; //超能力類對象

    /**
     * 這里使用依賴注入的方式
     * SimpleFactory constructor.
     * @param PowerInterface $type
     */
    public function __construct(PowerInterface $type)
    {
        $this->module = $type;
    }

    /**
     * 添加超能力類
     */
    public function addPower()
    {
        $this->module->setPower();
    }
}

抽象工廠模式

問題3:
  • 上面超能力和工廠比較少乎折,如果現(xiàn)在要一下生產(chǎn)大量并且具有多種超能力的超人。
  • 如果上一個方法沒有對工廠代碼使用依賴注入的話侵歇,添加一種超能力還得在超能力類型變量種添加一個參數(shù)骂澄;這樣的違反開閉原則(可以擴張,不能修改)惕虑;
解決:
  • 這里我們利用多個工廠——抽象工廠模式坟冲,定義一個抽象工廠,其他的在此上面擴展溃蔫,就不用修改原來的代碼健提。也不會違反開閉原則。
  • 生產(chǎn)多種超人——接口規(guī)范超人能力調(diào)用伟叛,防止不一樣
首先對工廠進行操作
  • 創(chuàng)建一個工廠方法抽象類FactoryMethod.php私痹,用來添加超能力
namespace Factory;

/**
 * 工廠方法抽象類
 * Class FactoryMethod
 * @package Factory
 */
abstract class FactoryMethod
{
    const JOG = 1;
    const FORCE = 2;

    /**
     * 子類實現(xiàn)啟動超能力方法
     * @param $type
     * @return mixed
     */
    abstract protected function activate($type);

    /**
     * 添加超能力
     * @param $type
     * @return PowerInterface a new power
     */
    public function addPower($type)
    {
        $obj = $this->activate($type);
        $obj->setPower();

        return $obj;
    }
}
  • 添加工廠1(FactoryOne.php)
namespace Factory;

use Exception;

/**
 * 工廠類1
 * Class FactoryOne
 * @package Factory
 */
class FactoryOne extends FactoryMethod
{
    protected function activate($type)
    {
        switch ($type) {
            case parent::JOG :
                return new Jog();
                break;
            case parent::FORCE :
                return new Force();
                break;
            default :
                throw new Exception("this type is not a valid power");
        }
    }
}
  • 添加工廠2(FactoryTwo.php)
namespace Factory;

use Exception;

/**
 * 工廠類2
 * Class FactoryTwo
 * @package Factory
 */
class FactoryTwo extends FactoryMethod
{
    public function activate($type)
    {
        switch ($type) {
            case parent::JOG :
                return new Jog();
                break;
            case parent::FORCE :
                return new Force();
                break;
            default :
                throw new Exception("this type is not a valid power");
        }
    }
}

工廠1和工廠2繼承工廠方法抽象類,實現(xiàn)創(chuàng)建超能力(工廠里邊存放實現(xiàn)超能力的對象)统刮,所以工廠實際上就是紊遵,把創(chuàng)建對象的過程封裝起來,隨時可以產(chǎn)生一個新的對象侥蒙。

然后就是對超能力的操作
  • 創(chuàng)建一個超能力的接口類PowerInterface.php(利用接口規(guī)范超能力)
namespace Factory;

/**
 * 超能力接口
 * Interface PowerInterface
 * @package Factory
 */
interface PowerInterface
{
    public function setPower();
}
  • 超能力1
namespace Factory;


class Force implements PowerInterface
{
    public function setPower()
    {
        echo "超能力:力量" . PHP_EOL;
    }
}
  • 超能力2
namespace Factory;


class Jog implements PowerInterface
{
    public function setPower()
    {
       echo "超能力:跑步";
    }
}

到這里工廠方法模式已經(jīng)完成了暗膜,現(xiàn)在我們先繼續(xù)把創(chuàng)建超人的代碼完成。

最后創(chuàng)建超人類Superman.php
namespace Factory;


class Superman
{
    protected $module;

    /**
     * Superman constructor.
     * @param FactoryMethod $factory
     */
    public function __construct(FactoryMethod $factory)
    {
        $this->module = $factory;
    }

    /**
     * 創(chuàng)建具有某種超能力的超人
     * @param $type
     */
    public function addSuperman($type)
    {
        $this->module->addPower($type);
    }
}

現(xiàn)在我們創(chuàng)建超人就非常方便了

<?php
use Factory\FactoryOne;
use Factory\FactoryTwo;
use Factory\Superman;

spl_autoload_register(function($class_name) {
    $class_file = realpath(dirname(__FILE__)."/../") .'/' . str_replace('\\','/', $class_name) . '.php';
    if (file_exists($class_file)) {
        include $class_file;
    }
});

//利用工廠1 創(chuàng)建超人(超能力是快跑)
$superman_1 = new Superman(new FactoryOne());
$superman_1->addSuperman(1);

//利用工廠2 創(chuàng)建超人(超能力是大力)
$superman_2 = new Superman(new FactoryTwo());
$superman_2->addSuperman(2);

定義多個工廠鞭衩,每個工廠生產(chǎn)不同的超人的超能力實例学搜,支持增加超能力類型,或工廠生產(chǎn)哪中超能力论衍。

創(chuàng)建超人的時候恒水,我們不用關注工廠是如何創(chuàng)建超人的,我們只關注工廠(工廠抽象方法類)提供的接口就可以了饲齐。這樣看來工廠就是將創(chuàng)造超能力(多個對象)的過程封裝起來

uml圖片可以參考這個

總結

工廠方法針對每一種產(chǎn)品提供一個工廠類钉凌,通過不同的工廠實例來創(chuàng)建不同的產(chǎn)品實例,在同一等級結構中捂人,支持增加任意產(chǎn)品(或者我可以把他理解成:將創(chuàng)建對象的過程封裝起來御雕,這樣對于外部關注抽象類矢沿,而不是具體的類)。這個模式同時也實現(xiàn)了依賴倒置酸纲。

我們經(jīng)常在數(shù)據(jù)庫的聯(lián)系捣鲸、支付接口或者用戶工廠的時候使用工廠模式。比如未來可能對應不同的支付網(wǎng)關:支付寶闽坡、財付通栽惶、網(wǎng)銀在線等。方便未來擴展,設計成工廠模式疾嗅。定一個專門生產(chǎn)網(wǎng)關接口的工廠外厂,抽象出來,做成接口形式,讓所有的子類都要實現(xiàn)它的接口代承。以后加一個支付方式汁蝶,要使用哪一種支付方式,改變一下參數(shù)即可论悴。

文章整理參考:PHP 設計模式系列 —— 工廠方法模式(Factory Method)

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末掖棉,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子膀估,更是在濱河造成了極大的恐慌幔亥,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件察纯,死亡現(xiàn)場離奇詭異紫谷,居然都是意外死亡,警方通過查閱死者的電腦和手機捐寥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進店門笤昨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人握恳,你說我怎么就攤上這事瞒窒。” “怎么了乡洼?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵崇裁,是天一觀的道長。 經(jīng)常有香客問我束昵,道長拔稳,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任锹雏,我火速辦了婚禮巴比,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己轻绞,他們只是感情好采记,可當我...
    茶點故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著政勃,像睡著了一般唧龄。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上奸远,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天既棺,我揣著相機與錄音,去河邊找鬼懒叛。 笑死丸冕,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的芍瑞。 我是一名探鬼主播晨仑,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼褐墅,長吁一口氣:“原來是場噩夢啊……” “哼拆檬!你這毒婦竟也來了?” 一聲冷哼從身側響起妥凳,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤竟贯,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后逝钥,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體屑那,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年艘款,在試婚紗的時候發(fā)現(xiàn)自己被綠了持际。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,953評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡哗咆,死狀恐怖蜘欲,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情晌柬,我是刑警寧澤姥份,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站年碘,受9級特大地震影響澈歉,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜屿衅,卻給世界環(huán)境...
    茶點故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一埃难、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦凯砍、人聲如沸箱硕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽剧罩。三九已至,卻和暖如春座泳,著一層夾襖步出監(jiān)牢的瞬間惠昔,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工挑势, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留镇防,地道東北人。 一個月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓潮饱,卻偏偏與公主長得像来氧,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子香拉,可洞房花燭夜當晚...
    茶點故事閱讀 44,901評論 2 355

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