特征访问 class 依赖是个坏主意吗?
Is trait accessing class dependency a bad idea?
我在 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");
}
}
}
并阅读其中一位回复者的评论:
Your trait, then is not a valid use-case: all of its users are
required, by definition, to add a $this->container property to its
dependencies, which will of course have an impact on that class'
contract and the contract of its children.
为什么作者声称这是一个糟糕的用例,如果这可能是某人需要的?就像如果某人几乎没有 class 都具有所需的依赖关系并且所有这些都重复出现相同的逻辑,他们是否应该只复制代码?
确实以这种方式使用了 trait - 一个坏主意。
如果有人决定在你的代码中使用这个特性——他必须确保 "container" 属性的存在。 "container" 应该是正确的类型(包括使用的方法)——否则会出错。实际上这段代码是不能重用的,而且这有潜在的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;
}
}
我同意这一点,我认为 traits 不应该使用 class 的任何方法或属性,但是当我阅读 laravel 框架源代码时,我看到很多滥用这个一些trait,比如InteractWithInput trait跟request是深度耦合的class,很迷惑
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;
}
}
我在 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");
}
}
}
并阅读其中一位回复者的评论:
Your trait, then is not a valid use-case: all of its users are required, by definition, to add a $this->container property to its dependencies, which will of course have an impact on that class' contract and the contract of its children.
为什么作者声称这是一个糟糕的用例,如果这可能是某人需要的?就像如果某人几乎没有 class 都具有所需的依赖关系并且所有这些都重复出现相同的逻辑,他们是否应该只复制代码?
确实以这种方式使用了 trait - 一个坏主意。 如果有人决定在你的代码中使用这个特性——他必须确保 "container" 属性的存在。 "container" 应该是正确的类型(包括使用的方法)——否则会出错。实际上这段代码是不能重用的,而且这有潜在的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;
}
}
我同意这一点,我认为 traits 不应该使用 class 的任何方法或属性,但是当我阅读 laravel 框架源代码时,我看到很多滥用这个一些trait,比如InteractWithInput trait跟request是深度耦合的class,很迷惑
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;
}
}