特征访问类依赖项是个坏主意吗


Is trait accessing class dependency a bad idea?

我在Stackeexchange上看到了一个例子(请注意访问类属性的特性):

trait CheckPermissionTrait
{
    protected function checkPermission($object_id)
    {
        $judge = $this->container->get('acme_judge');
        $user  = $this->container->get('security.context')->getToken()->getUser();
        if( !$judge->isPermitted($user, $object_id) ) {
            throw $this->createAccessDeniedException("Brabbel");
        }
    }
}

阅读回复者的评论:

那么,你的特征就不是一个有效的用例:它的所有用户都是根据定义,需要将$this->容器属性添加到其依赖项,这当然会对该类产生影响合同及其子女的合同。

为什么作者声称这是一个糟糕的用例,如果这可能是某人所需要的?比如,如果有人有几个类都具有所需的依赖关系,并且所有类中都有相同的逻辑记录,那么他们应该保持代码重复吗?

确实,以这种方式使用了trait-这是个坏主意。如果有人决定在你的代码中使用这个特性,他必须确保"容器"属性的存在。"container"应该是正确的类型(包括所使用的方法),否则会出错。事实上,这段代码是不能重复使用的,这可能会导致错误。此外,它违反了SOLID规则的规则DIP(依赖反转原理)。

有可能绕过这个:

interface ExampleContainerInterface{
}
trait CheckPermissionTrait
{
    protected $container;
    public function __construct(ExampleContainerInterface $container)
    {
        $this->container = $container;
    }
    protected function checkPermission($object_id)
    {
        $judge = $this->container->get('acme_judge');
        $user  = $this->container->get('security.context')->getToken()->getUser();
        if( !$judge->isPermitted($user, $object_id) ) {
            throw $this->createAccessDeniedException("Brabbel");
        }
    }
}
class ExampleClassA
{
    use CheckPermissionTrait;
}
class ExampleClassB
{
    use CheckPermissionTrait;
}

或者像这样(php7):

interface ExampleContainerInterface{
}
trait CheckPermissionTrait
{
    abstract public function getContainer():ExampleContainerInterface;
    protected function checkPermission($object_id)
    {
        $container = $this->getContainer();
        $judge = $container->get('acme_judge');
        $user  = $container->get('security.context')->getToken()->getUser();
        if( !$judge->isPermitted($user, $object_id) ) {
            throw $this->createAccessDeniedException("Brabbel");
        }
    }
}
class ExampleClassA
{
    use CheckPermissionTrait;
    protected $container;
    public function getContainer():ExampleContainerInterface
    {
        return $this->container;
    }
}
class ExampleClassB
{
    use CheckPermissionTrait;
    protected $container;
    public function getContainer():ExampleContainerInterface
    {
        return $this->container;
    }
}

我同意这一点,我不认为traits应该使用类的任何方法或属性,但当我阅读laravel框架的源代码时,我看到了许多这种traits的滥用,例如,InteractiveWithInput trait与请求类深度耦合,这让非常困惑

trait InteractsWithInput
{
    //the getInputSource method is defined in Request class
    public function input($key = null, $default = null)
    {
        return data_get(
            $this->getInputSource()->all() + $this->query->all(), $key, $default
        );
    }
}
class Request
{
    use InteractsWithInput
    protected function getInputSource()
    {
        if ($this->isJson()) {
            return $this->json();
        }
        return in_array($this->getRealMethod(), ['GET', 'HEAD']) ? $this->query : $this->request;
    }
}