升級工廠前的準備作業
無規矩不成方圓,隨著越來越多的行為出現,我們需要需要定下一些規范,
為了約束每一個行為的規范,需要定義一個行為介面:
interface BehaviorInterface
{
/**
* 行為激活方法
* @param array $values 激活的引數
*/
public function activate(array $values);
}
按照介面規范修改前述的行為類:
class Attack implements BehaviorInterface{
protected $value = https://www.cnblogs.com/danhuang/p/0;
public function __construct($value)
{
$this->value = $value;
}
public function activate(array $values){}
}
class Defend implements BehaviorInterface{
protected $value = 0;
public function __construct($value){}
public function activate(array $values){}
}
class Move implements BehaviorInterface{
protected $speed;
public function __construct($speed){}
public function activate(array $values){}
}
class Skill1 implements BehaviorInterface{
protected $name ='暴擊';
public function __construct(){}
public function activate(array $values){}
}
class Skill2 implements BehaviorInterface{
protected $name = '眩暈';
public function __construct(){}
public function activate(array $values){}
}
使用依賴注入升級工廠
- 為了解決工廠對行為的依賴,在每一種行為定義好之后就放到工廠中(依賴注入),
- 放到工廠中的行為必須是依照規范定義的,
class BehaviorFactory
{
protected $instances;
public function setBehavior($behaviorName, $behavior)
{
if($behavior instanceof BehaviorInterface) {
$this->instances[$behaviorName] = $behavior;
}
}
public function getBehavior($behaviorName)
{
return $this->instances[$behaviorName];
}
}
$factory = new BehaviorFactory();
$factory->setBehavior('Attack', new Attack(0));
$factory->setBehavior('Defend', new Defend(0));
$factory->setBehavior('Move', new Move(0));
$factory->setBehavior('Skill1', new Skill1());
$factory->setBehavior('Skill2', new Skill2());
對英雄類做一些簡單的修改:
class Hero
{
protected $behavior = [];
public function __construct($factory, array $behaviors)
{
// 通過工廠提供的方法制造需要的模塊
foreach ($behaviors as $behaviorName => $behaviorOptions) {
$behavior = $factory->getBehavior($behaviorName);
$this->behavior[] = $behavior->activate($behaviorOptions);
}
}
}
$hero = new Hero($factory, [
'Attack' => [10],
'Defend' => [5],
'Move' => [30],
'Skill1' => [],
]);
至此,我們通過依賴注入,已經徹底地解決了英雄對行為的依賴、英雄對工廠的依賴、工廠對行為的依賴,
但是還有一些不足之處:
- 所有的物件,我們都必須手動去實體化;
- 這些行為,無論英雄是否需要,都要提前實體化并放到工廠中,
再談依賴注入
前面的文章中我們已經講解了如何通過PHP的反射機制來解決依賴注入的問題,這里我們再來深入一下對依賴注入的理解,
什么是依賴注入:
所謂的依賴注入,意思是只要不是在類內部產生的物件依賴(比如在初始化、建構式中,通過工廠方法或者手動實體化),而是由外部以引數或其他形式注入(傳入)的,都屬于依賴注入(DI),
超級工廠-IOC服務容器
接下來將我們的升級工廠,進一步改造為超級工廠,
定義一個簡單的容器類,用來存盤物件實體化的方式和創建物件:
class Container
{
// 存放抽象類與物件實體化方式關系的資料
protected $binds;
// 存放抽象類與已有的物件關系的資料
protected $instances;
// 將抽象類與[物件實體化方式|已有的物件]分別存入陣列
public function bind($abstract, $concrete)
{
// 如果第二個引數是閉包,則存入 $binds 陣列
if ($concrete instanceof Closure) {
$this->binds[$abstract] = $concrete;
}
// 如果第二個引數不是閉包,則為已經實體化的物件,存入 $instances 陣列
else {
$this->instances[$abstract] = $concrete;
}
}
// 根據物件實體化的方式創建物件并回傳
public function make($abstract, $parameters = [])
{
// 如果是已經實體化的物件則直接回傳
if (isset($this->instances[$abstract])) {
return $this->instances[$abstract];
}
// 將$this(容器)放入引數陣列的頭部
// [$this, $param1, $param2...]
array_unshift($parameters, $this);
// 呼叫物件實體化的方法(即系結時傳入的閉包函式和當前的引數),得到實體化后的物件并回傳
// function($container, $behaviorName) { return new Hero($behavior); }
// function($this, 'skill1') { return new Hero('skill1'); }
return call_user_func_array($this->binds[$abstract], $parameters);
}
}
測驗這個容器:
// 創建一個容器
$container = new Container;
// 向該容器添加英雄的生產方式(實體化的方式)
$container->bind('hero', function($container, $behaviorName) {
$behavior = $container->make($behaviorName);
return new Hero($behavior);
});
// 向該容器添加行為的生產方式(實體化的方式)
$container->bind('skill1', function($container) {
return new Skill1;
});
// 同上
$container->bind('skill2', function($container) {
return new Skill2;
});
// 開始啟動生產
$hero1 = $container->make('hero', ['skill1']);
$hero2 = $container->make('hero', ['skill2']);
在這個容器中,我們通過系結操作,可以向超級工廠注冊很多各種各樣的生產腳本(可以是匿名函式、非匿名函式、類的方法),這些腳本在生產操作觸發的時候就會被容器呼叫執行,
通過依賴注入和容器,我們徹底解決了所有物件之間的依賴關系;最重要的是,容器類也沒有和英雄、行為之間有任何的依賴,另外也不需要再去手動實體化物件,還實作了按需實體化,
實際上,真正的 IoC 容器更為高級,會根據類的依賴需求,使用PHP的反射(Reflection)機制,自動在注冊、系結的一堆實體中搜尋符合的相關依賴,并自動注入到建構式引數中去,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/17471.html
標籤:PHP
上一篇:微信支付jsapi
下一篇:Swoole 中使用異步任務
