在一个特质中使用类的属性。

huangapple go评论92阅读模式
英文:

Using properties of a class inside a trait

问题

我刚刚遇到了有关特性(traits)的意外行为。

在Symfony 6.2项目中,我在控制器内部使用了一个特性。当调用$this->getUser()或$this->isGranted()时,它实际上会正确执行并获取用户或投票结果,就像它在控制器类内部执行一样。

显然,特性的代码在控制器类的上下文中执行,因此在这种情况下,特性内部可以使用控制器的属性/方法。

这是预期的行为还是我不应该依赖的副作用?

伪代码:

class SomeController extends AbstractController
{
    use SomeTrait;

    #[Route('/api/some-route', methods:['GET'])]
    public function doSomething(){
        // 代码
        $this->callTraitMethod();
        // 代码
    }
}

trait SomeTrait
{
    public function callTraitMethod()
    {
        // 代码
        $this->getUser(); // 正确工作!
        // 代码
    }
}
英文:

I just came across an unexpected behavior regarding traits.

In a Symfony 6.2 project I use a trait inside a Controller. When calling $this->getUser() or $this->isGranted() it will actually perform correctly and fetch the user or the voter result, just like if it was executed inside the Controller class.

Obviously the code of the trait is executed in the context of the Controller class, so the attributes / methods of the Controller are available inside the trait in this scenario.

Is this intended behavior or just a side effect which I should not rely on?

Pseudo Code:

class SomeController extends AbstractController
{
    use SomeTrait;

    #[Route('/api/some-route', methods:['GET'])]
    public function doSomething(){
        // code
        $this->callTraitMethod();
        // code
    }
}

trait SomeTrait
{
    public function callTraitMethod()
    {
        // code
        $this->getUser(); // works correctly !
        // code
    }
}

答案1

得分: 2

这是预期行为,而不是副作用。

使用(故意)这种语法,类使用特质。类也通过_use_控制覆盖。

因此,特质实际上只是模板代码。

你_可以_遵循Barbara Liskov的理解,即抽象(超)类根本不应该包含受保护的属性,然后将其应用于特质。虽然这不是一一对应的,但你可以设计仅在特质代码内部使用方法,而从不使用属性(既不定义也不访问),以更好地为抽象设计模板代码。

"特质在运行时是复制粘贴。"(我不记得我最早在哪里听说的了,如果有人记得,请留言。)


然后你在评论中澄清了:

>我猜在编码时依赖于使用这个特质的类的实现并不是一个好的做法。或者它实际上就是用来这样使用的?

只要你依赖于抽象类接口,我会说这是常见的做法,每天在全世界各地都有数百万人这样做。你可能会想要质疑特质是否是一个好的做法,以及是否要依赖其中定义的模板,这样也许更有意义,但特质从未起主导作用,一个使用了错误特质的类是一个有问题的类,而不是一个依赖于类实现的特质。一个特质只是一个特质。

你可以根据自己的需要使用它。

英文:

> Obviously the code of the trait is executed in the context of the Controller class, so the attributes / methods of the Controller are available inside the trait in this scenario.
>
> Is this intended behavior or just a side effect which I should not rely on?

This is intended behavior and not a side effect.

Use (pun intended) the syntax as a mnemonic, the class uses the trait. The class by use also controls overrides.

So the trait effectively is template code only.

You could follow the understanding of Barbara Liskov that an abstract (super)class should not contain protected properties at all and transpose that onto traits. While this does not fit 1:1, you could design to only use methods within the traits's code and never properties (both not defining nor accessing), to design better for abstraction while you write template code.

"Traits are copy and paste at runtime." (I don't remember where I heard it first, so if anyone remembers, leave a comment.)


You then clarified in a comment:

> I guess coding relying on implementations of a class that is using this trait is not a good practise. Or is it actually intended to be used like that?

As long as you rely on the abstract class interface, this is common practice I'd say and done day-to-day in the millions all around the world. You may wanna question if traits are good practice and if you want to rely on the templates defined therein, then it perhaps makes more sense, but the trait is never leading, and a class using the wrong trait is a broken class, not a trait relying on implementations of a class. A trait just is.

You can use it however it suits you.

答案2

得分: 0

这是因为特质方法可以访问$this->getUser()$this->isGranted(),就好像它们是直接从控制器类中调用的一样,这是预期的行为。这是因为特质实质上与它们在其中使用的类“合并”,它们与类共享相同的作用域。

当您在SomeController类中使用SomeTrait时,特质中定义的方法成为控制器类的一部分,$this引用SomeController的实例,这就是为什么您可以在特质方法中轻松访问$this->getUser()$this->isGranted()而不会出现任何问题。

英文:

The behaviour you observed, where the methods from the trait can access $this->getUser() and $this->isGranted() as if they were called directly from the controller class, is intended behavior. This is because traits are essentially "merged" with the class they are used in, and they share the same scope as the class.

When you use the SomeTrait in the SomeController class, the methods defined in the trait become part of the controller class, and $this refers to the instance of SomeController, which is why you can access $this->getUser() and $this->isGranted() from within the trait methods without any issues.

huangapple
  • 本文由 发表于 2023年7月23日 18:32:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/76747763.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定