Что, если класс адаптера должен быть подсказан по типу?(РНР) - PullRequest
0 голосов
/ 31 января 2019

базовый, рабочий пример:

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 не вариант ...

1 Ответ

0 голосов
/ 31 января 2019

Вы можете создать встроенный класс, который расширяет 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

...