特质访问类依赖是一个坏主意吗?

nan*_*eri 6 php oop traits

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

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");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

并阅读其中一位回复者的评论:

那么你的特性不是一个有效的用例:根据定义,它的所有用户都需要在其依赖项中添加一个$ this-> container属性,这当然会对该类的合同和合同产生影响.它的孩子们.

为什么作者声称这是一个糟糕的用例,如果这可能是某人需要的?就像有人有几个类都具有所需的依赖关系并且所有这些类中都有相同的逻辑重新组合一样,它们是否应该保持代码重复?

Max*_*ach 2

确实,以这种方式使用特质 - 一个坏主意。如果有人决定在您的代码中使用此特征 - 他必须确保“容器”属性的存在。“容器”应该是正确的类型(包括使用的方法) - 否则会出错。事实上这个代码不能被重用,并且这个潜在的bug。另外,它违反了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;
}
Run Code Online (Sandbox Code Playgroud)

或者像这样(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;
    }
}
Run Code Online (Sandbox Code Playgroud)