装饰模式问题 - 如何调用嵌套装饰器方法?

时间:2016-02-28 19:02:32

标签: oop object design-patterns nested decorator

我现在正在研究装饰器模式,这里是一些示例代码(PHP):

abstract class component{

  public function drawShape(){};

}

class concreteComponent extends component{

   public function drawShape(){//code};

}

class decoratorComponent extends component{

  private $component;

  public function __construct($component){ $this->component=$component; }

  public function drawShape(){

      $this->component->drawShape();

  }

}

class borderDecorator extends decoratorComponent{

  public function drawShape(){

    $this->drawBorder();
    $this->component->drawShape();

  }

  public function setBorder(){};
  public function drawBorder(){};

}

class bgColorDecorator extends decoratorComponent{

  public function drawShape(){

    $this->drawBgColor();
    $this->component->drawShape();

  }

  public function setbgColor(){};
  public function drawBgColor(){};

}

好的,现在:

$test=new concreteComponent();
$border=new borderDecorator($test);
$border->setBorder(10);
$bgColor= new bgColorDecorator($border);
$bgColor->setBgColor(#000);

现在我有一个装饰有#000 bg颜色和10(某些单位)边框的组件。

$bgColor->drawShape(); 

表示drawBgColor + drawBorder + drawShape并且没错,但是:

如何修改或删除边框??

$bgColor-> ???

bgColor类无法直接访问边框方法...

由于

2 个答案:

答案 0 :(得分:0)

根据我的理解,你已经将一个bgColorDecorator实例与你无法设置/删除边框的方式链接了你的装饰器。

你应该做的是改变构造的顺序并由borderDecorator部分完成:

$test=new concreteComponent();

$bgColor= new bgColorDecorator($test); // pass test to bgcolor
$bgColor->setBgColor(#000);

$border=new borderDecorator($bgColor); // pass bgcolor to border
$border->setBorder(10);

// You can now set/remove border on $border
// and of course : $border->drawShape();

您的任务似乎是渲染对象,因此正确的绘制顺序应该需要更改drawShape方法以保持顺序背景/形状/边框

$this->drawBgColor();
$this->component->drawShape();
// will become a post-action
$this->component->drawShape();    
$this->drawBgColor();

现在问题是你因为同样的原因而无法动态设置backgroundcolor 。所以另一个解决方案可能是修改你的decoratorComponent接口以包含你需要的东西并在decoratorComponent子类中实现它。

编辑双边框案例:

将两个borderDecorator链接到一个Component

$cmp = new concreteComponent();

$bDecrtor1 = new borderDecorator($cmp); // 1st border decorator on cmp
$bDecrtor1 ->setBorder(10);

$bDecrtor2=new borderDecorator($bDecrtor1); // 2nd border decorator on 1st one
$bDecrtor2->setBorder(20);

// $bDecrtor2->drawShape();
// You can then use bDecrtor1 or bDecrtor2 to (re)set the border properties
// You can use bDecrtor2 to chain other decorators...

答案 1 :(得分:0)

你可以做的是实现名为magic method__call()(一个包罗万象的方法),尝试将任何不存在的方法委托给包裹的component

class decoratorComponent extends component{

  private $component;

  /* ... */

  public function __call( $name, $arguments ) {
    return call_user_func_array( array( $this->component, $name ), $arguments );
  }
}

您必须建立安全措施,以便了解您尝试调用的方法最终不存在于任何组件中的情况。

但是,您可能还应该评估是否甚至需要在之后调用装饰器方法,然后将它们包装在另一个装饰器中。