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

问题描述 投票:0回答:3

我在 Stackexchange 上看到过一个示例(请注意访问类属性的特征):

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->container 属性添加到其 依赖关系,这当然会对该类产生影响' 合同及其子合同。

如果有人需要的话,为什么作者声称这是一个糟糕的用例?就像如果某人有几个类都具有所需的依赖项,并且所有类中都重复出现相同的逻辑,那么他们是否应该保留代码重复?

php oop traits
3个回答
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;
}

或者像这样(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;
    }
}

1
投票

我同意这一点,我认为特征不应该使用类的任何方法或属性,但是当我阅读 Laravel 框架源代码时,我看到很多对这种特征的滥用,例如,InteractWithInput 特征深深地再加上request类,就很混乱了

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;
    }
}

0
投票

或者您可以使用方法注入并且不违反任何内容:

trait CheckPermissionTrait
{
    protected function checkPermission($object_id, $container)
    {
        $judge = $container->get('acme_judge');

        $user  = $container->get('security.context')->getToken()->getUser();

        if( !$judge->isPermitted($user, $object_id) ) {
            throw $this->createAccessDeniedException("Brabbel");
        }
    }
}

显然,当有人想要使用这个特征时,他必须在方法调用中提供容器作为参数。现在一切都已保存。

© www.soinside.com 2019 - 2024. All rights reserved.