工廠模式
在講解工廠模式之前,我們先來探討一些問題谢翎,研究是為什么會出現(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)造超能力(多個對象)的過程封裝起來
總結
工廠方法針對每一種產(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ù)即可论悴。