一个基本的可行示例:
class Test
{
public function test()
{
return 'a';
}
}
/**
* @mixin Adapter
*/
class TestAdapter
{
/**
* @var Test
*/
private $test;
public function __construct(Test $test)
{
$this->test = $test;
}
public function __call($method, $args)
{
switch($method)
{
case 'test' :
return 'decorated: '.$this->test();
default :
throw new \Exception('Unhandled method call: '.$method);
}
}
}
$test = new Test();
$testAdapter = new TestAdapter($test);
$testAdapter->test();
到目前为止,一切都很好。但是,如果某人需要此Test
怎么办?如果抽象到位怎么办?
abstract class TestAbstract
{
public abstract function t();
}
class Test extends TestAbstract
{
public function t()
{
return 't';
}
public function test()
{
return 'test';
}
}
class WannaTest
{
public function __construct(Test $test)
{
}
}
这种方式:
$test = new Test();
$testAdapter = new TestAdapter($test);
$wannaTest = new WannaTest($testAdapter); // would throw fatal!
这将不起作用,因为WannaTest
期望Test
。
当然我可以扩展TestAdapter
:
class TestAdapter extends Test
{
public function t()
{
// now I cant mock it!
}
}
但是在那种情况下,如果我有10个抽象方法,即使只使用其中一个,我也必须实现它们。这样,我既不能使用__call
作为代理。所以有点臭。如何解决?不能删除typehint ...
答案 0 :(得分:1)
您可以创建扩展Test
的内联类,并根据需要装饰该方法。这是一个例子。
class TestDecorator //decorator sounds more appropriate
{
public static function decorate(Test $test) {
return new class($test) extends Test {
private $test;
public function __construct(Test $test) {
$this->test = $test;
}
public function test() {
return 'decorated: '.$this->test->test();
}
};
}
}
$test = new Test();
$decorated = TestDecorator::decorate($test);
echo $decorated->test();
类型提示Test
现在应该可以正常工作,因为修饰的类确实扩展了Test