使用PHP中的Decorator模式动态实例化类

时间:2017-07-27 21:32:42

标签: php arrays design-patterns

通常,在使用装饰器模式时,我通常直接实例化,一个在另一个中:

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个处理程序,您可以看到代码会开始缩进太多并且难以阅读,特别是如果链中的不同处理程序不在&#39 ; t的名称简单如" FirstHandler"," SecondHandler",但总的来说,它看起来并不好。

我想知道是否有办法通过将类放在数组中来动态实例化类,并迭代数组并将数组中的 n + 1 元素作为构造函数参数传递给数组中的 nth 元素。

我心中有这样的想法:

$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)

以下是在构造函数中使用数组的示例,并使用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 );