2017-07-27 35 views
1

典型地,具有裝飾圖案的工作時,我通常實例化的類直接,一個在另一個內:動態實例化使用裝飾圖案類在PHP

abstract class Handler 
{ 
    public function __construct(Handler $handler = null) 
    { 
     $this->handler = $handler; 
    } 

    abstract public function handle(); 
} 

class FirstHandler extends Handler 
{ 
    public function handle() 
    { 
     // Just calls the next guy in the chain. Obviously, this isn't doing anything, it's just for example sake. 
     $this->handler->handle(); 
    } 
} 

// ... and so on with the SecondHandler, ThirdHandler, FourthHandler 

$chain = new FirstHandler(
    new SecondHandler(
     new ThirdHandler(
      new FourthHandler() 
     ) 
    ) 
); 

$chain->handle(); 

然而,與鏈條的可能性越來越多的人說,可能有20個處理程序,你可以看到代碼開始縮進太多並且難以閱讀,特別是如果鏈中的不同處理程序的名稱不像「FirstHandler」,「SecondHandler 「,但總體而言,它看起來不太好。

我想知道如果有一種方法,以通過將它們放置在陣列中,並且迭代向下陣列並傳遞n + 1個元件陣列中的作爲構造參數傳遞給第n動態實例化的類數組中的元素。

我腦子裏想的東西有點像這樣:

$chain = null; 

$links = [ 
    FirstHandler::class, 
    SecondHandler::class, 
    ThirdHandler::class, 
    FourthHandler::class 
]; 

foreach ($links as $index => $link) { 
    $chain = new $link(new $links[$index + 1]()); 
} 

您也許能夠告訴大家,第一個問題是$chain大幹快上的每個呼叫覆蓋。第二個問題是,即使在循環中,我仍然必須手動將每個處理程序傳遞到鏈中,而實際上所做的只是創建鏈四次,這實際上更糟糕。第三個問題是最終我會遇到一個越界異常。

有沒有一種方法來動態地實例化這個鏈(如我的意圖所示)(或許是一種遞歸調用)還是我註定要按照之前的方式做到這一點?

+1

你要創造**依賴注入** –

+0

哈哈。你能用一個例子來證明這一點嗎?我想了解我是如何做到的。 –

+0

谷歌它。這包括準備使用像DI容器等組件。所有的例子都在 –

回答

0

下面是在構造函數中使用數組並使用array_shift從列表中刪除第一個項目的示例。 Handler::makeChain($classes)得到球滾動...

class Handler 
{ 
    private $handler; 
    public function __construct($chain) 
    { 
    if(empty($chain) === false) 
    { 
     $this->handler = static::makeChain($chain); 
    } 
    } 
    public static function makeChain($chain) 
    { 
    $class = array_shift($chain); 
    return new $class($chain); 
    } 
} 

class FirstHandler extends Handler{} 
class SecondHandler extends Handler{} 
class ThirdHandler extends Handler{} 
class FourthHandler extends Handler{} 

$classes = array(
    'FirstHandler', 
    'SecondHandler', 
    'ThirdHandler', 
    'FourthHandler', 
); 

$handler = Handler::makeChain($classes); 

var_dump($handler); 

// object(FirstHandler)#77 (1) { 
// ["handler":"Handler":private]=> 
// object(SecondHandler)#78 (1) { 
// ["handler":"Handler":private]=> 
//  object(ThirdHandler)#79 (1) { 
//  ["handler":"Handler":private]=> 
//  object(FourthHandler)#80 (1) { 
//  ["handler":"Handler":private]=> 
//   NULL 
//  } 
//  } 
// } 
// } 

第二版與__construct typehint

class Handler 
{ 
    private $handler; 
    public function __construct(Handler $injectedHandler = null) 
    { 
    if($injectedHandler) 
    { 
     $this->handler = $injectedHandler; 
    } 
    } 
    public static function handlerFactory($chain) 
    { 
    $instance = null; 
    foreach(array_reverse($chain) as $class) 
    { 
     $instance = new $class($instance); 
    } 
    return $instance; 
    } 
} 

class FirstHandler extends Handler{} 
class SecondHandler extends Handler{} 
class ThirdHandler extends Handler{} 
class FourthHandler extends Handler{} 

$classes = array(
    'FirstHandler', 
    'SecondHandler', 
    'ThirdHandler', 
    'FourthHandler', 
); 

$handler = Handler::handlerFactory($classes); 

var_dump($handler); 
+1

這是一個非常好的答案,它真的*接近解決我的需求。對我來說唯一的問題是,這將需要我提升我對構造函數的typehinting的限制,我一直保證總是傳遞一個'Handler'類型的實例。 –

+0

使用'Handler :: handlerFactory($ classes)'嘗試我的第二個版本''使用'array_reverse($ chain)' – Scuzzy