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

时间:2016-05-11 09:24:13

标签: 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");
        }
    }
}

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

  

您的特性,然后不是一个有效的用例:它的所有用户都是   根据定义,需要向其添加$ this->容器属性   依赖性,这当然会对该类产生影响。   合同和子女的合同。

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

1 个答案:

答案 0 :(得分:1)

确实以这种方式使用特征 - 一个坏主意。 如果有人决定在你的代码中使用这个特性 - 他必须确保存在“容器”属性。 “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;
    }
}