使用Symfony的EventDispatcher组件的正确方法是什么? [英] what's the proper way to use Symfony's EventDispatcher component?

查看:85
本文介绍了使用Symfony的EventDispatcher组件的正确方法是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我希望通过使某些类可以观察到来促进我的PHP代码松散耦合。 Symfony的 EventDispatcher组件看起来很有前途,SPL SplObserver / SplSubject也是如此对课程。



最好的方法是什么?我可以看到几种不同的可能性:


(1)将EventDispatcher实例注入每个可观察类(跟踪全局EventDispatcher实例):

  class Foo 
{
public function __construct($ dispatcher)
{
$ this-> dispatcher = $ dispatcher;
}

public function bar()
{
$ this-> dispatcher-> dispatch(...);
}
}

(2)让observable类扩展EventDispatcher类:

  class Foo extends EventDispatcher 
{
public function bar()
{
$ this-> dispatch(...);
}
}

(3)使用SplObserver / SplSubject - 直接,不如EventDispatcher组件那么灵活

解决方案

免责声明:这个答案与Symfony EventDispatcher无关,做你的问题如果你只想要一个答案,你可以跳过(有点)学术讨论并跳到最后。



讨论




FACT :增加应用程序大小意味着复杂性的一致增加。


随着应用程序的范围越来越大,您会发现自己添加了越来越多的类来实现必要的功能。突然之间,记住一个 Foo 对象需要执行一些特定的操作,当一个 Bar 对象被创建。此外,随着您的对象开始相互提供互补功能,变得越来越难以保持必要的关系,而不会导致非常紧密耦合的对象。



我们需要一种方式对象进行通信,无需硬编码显式引用,我们将在某些更改时忘记更改。那么我们如何在快速增长的对象图的节点之间管理这种相互关联的功能?



如果你想要最后,你有话要说



我们稍稍绕弯一下,考虑一个浪漫的比喻...



任何关系都需要一致的沟通持续当然,你和你的伴侣可以在星期六晚上聚在一起睡觉,不要在周末休息。然而,这种沟通通常导致脆弱的关系,双方都不了解其他人在关系背景下实际需要什么功能。



继续比喻,随着您的个性随着时间的推移而逐渐变化(并将会发生),这种缺乏沟通将阻止您的伴侣了解如何与您进行最佳交互。最终,所有的破碎的承诺和错过的电话都站了起来,关系不再奏效了。它是坏的。



您的应用程序以相同的方式工作。代码应该足够成熟,可以说:宝贝,我可能会改变,但如果我这样做,我保证,我会永远告诉你,发生了什么事情。不幸的是,随着复杂性的增加,传统的直线应用设计使得这种沟通难以维持,而不会出现类之间的紧密联系。



输入事件管理



这是事件管理的一切。目标是为我们的对象提供一种相互沟通的方式,不会与他们需要沟通的对象硬编码关系。与大多数编程问题一样,没有一个单一的,具体的正确的方法来做到这一点。你的问题具体提到了两个可以实现的方法,所以我会解决这些问题。如果您想了解其他一些选项,@ircmaxell最近发布了一个关于制作PHP应用程序可插拔的很好的调查博客文章



观察者



练习你会发现几乎没有现实的PHP应用程序的Observer模式。这是因为如果您希望您的代码非常动态,那么在将观察者附加到所有对象之前不需要花费很长时间。



发生这种情况您已经开始尝试实现松耦合,但是您已经创建了一个不同的问题:手动附加所有观察者和主题。例如,如果您的应用程序中的每个类都是 Logger 观察器对象的主题,则为自己创建了大量工作。此外,这种方法有时会通过移动可能更准确地描述为主题构造方法签名的主题的实际依赖关系来掩盖您的API。



如果我们使用集中式调度员在事件发生时通知有兴趣的对象,我们的应用将会更加灵活,尽管观察者模式可以成为一次性或简单情况下的理想选择。



Mediator



更强大的管理事件的方法是通过插入一个集中层来处理调度事件到适当的监听器。这是中介者 维基 模式(和Symfony事件调度员)。



中介者的观点是它是系统中每个事件的集中式中转站,因此需要可以访问整个应用范围(或介绍的部分,无论如何)。请注意,此表示您应将其视为全局变量,并使用全局关键字访问Mediator,或将其包装在某种邪恶的单例对象或静态属性/方法中。这种滥用将导致第一个答案中提出的问题@liquorvicar。然而,我非常不同意这个答案的评估:


有一个eventDispatcher无处不在你的应用程序,几乎所有的东西都可以使你的代码更难测试/理解/维护等(可以接近上帝的对象)


只有当您滥用Mediator ;它应该调度事件通知,没有别的。出于这个原因,我谨提醒您不要像您在问题的选项(2)中所示的那样扩展它。当正确使用时,调解对象是非常可测试的。没有比嘲笑构造函数中指定的依赖对象的行为更简单。



答案



所以,如果你需要非线性事件管理在您的申请中,我强烈建议您从您的问题中选择(1)。只要你不滥用它,这是完全可以接受的。在Symfony实现上,它似乎支持任何PHP作为监听器调用。就个人而言,我喜欢一种允许基于类的监听器的延迟实例化的系统,用于更有效率和面向对象的范例,但实现细节取决于您。



责任链模式与调解员密切相关,是实现类似结果的另一种有效方法。如果你有兴趣,我会建议早些时候发布的链接@ ircmaxell的博文。


I'd like to promote loose coupling in my PHP code, by making certain classes observable. Symfony's EventDispatcher component looks promising, as does the SPL SplObserver/SplSubject pair of classes.

What's the best way to do this? I can see a couple of different possibilities:

(1) Inject an EventDispatcher instance into each observable class (keeping track of a global EventDispatcher instance):

class Foo
{
    public function __construct($dispatcher)
    {
        $this->dispatcher = $dispatcher;
    }

    public function bar()
    {
        $this->dispatcher->dispatch(...);
    }
}

(2) Have the observable class extend the EventDispatcher class:

class Foo extends EventDispatcher
{
    public function bar()
    {
        $this->dispatch(...);
    }
}

(3) Use SplObserver/SplSubject - straightforward, but not as flexible as the EventDispatcher component

解决方案

Disclaimer: this answer has nothing to do with the Symfony EventDispatcher but everything to do with your question. If you just want an answer you can skip the (somewhat) academic discussion and jump to the end.

Discussion

FACT: Increasing application size means a congruent increase in complexity.

As the scope of your application grows you find yourself adding more and more classes to implement the requisite functionality. All of a sudden, it's not so easy to remember that a Foo object needs to perform some specific action when a Bar object is created. Further, as your objects start providing complimentary functionality to one another it becomes increasingly more difficult to maintain the necessary relationships without ending up with very tightly coupled objects.

We need a way for objects to communicate without hard-coding explicit references that we'll forget to alter when something changes. So how do we manage this kind of interrelated functionality between nodes of a rapidly growing object graph?

If You Want It to Last, You've Gotta Talk

Let's take a slight detour for a moment and consider a romantic metaphor ...

Any relationship requires consistent communication if it's going to last. Sure, you and your partner can get together for shady hookups on Saturday night and not talk to each other the rest of the week. However, this type of communication usually results in a brittle relationship where neither party understands what the other actually needs to function well in the context of the relationship.

Continuing the analogy, as your personality slowly changes over time (and it will), this lack of communication prevents your partner from understanding how best to interact with you. Eventually, all the broken promises and missed calls come to a head and the relationship just doesn't work anymore. It's broken.

Your application works in the same way. Code should be mature enough to say, "Hey baby, I might change, but if I do, I promise I'll always let you know what's going on with me." Unfortunately, as complexity increases, traditional straight-line application design makes this communication difficult to maintain without tight coupling between classes.

Enter Event Management

And this is what event management is all about. The goal is to provide our objects a means to communicate with each other in a way that doesn't hard-code relationships to the objects they need to communicate with. As with most programming problems, there's not a singular, specific, "correct" way to do this. Your question specifically mentions two of the available methods for accomplishing this, so I'll address those. If you want to learn about some of the other options, @ircmaxell recently posted a nice survey blog post about making PHP apps "pluggable".

Observer

In practice you'll find few real-world PHP applications for the Observer pattern. This is because if you want your code to be very dynamic it doesn't take long before you're attaching observers to subject objects all over the place.

When this happens, you've got the loose coupling you started out trying to achieve, but you've created a different sort of problem: manually attaching all the observers and subjects. For example, you've created a lot of work for yourself if every class in your application is the subject of a Logger observer object. Also, IMHO this method sometimes obscures your API by moving things that might be more accurately described as actual dependencies of the subject out of the subject constructor's method signature.

Our application would be much more flexible if we used a centralized dispatcher to notify interested objects when an event occurs, though the Observer pattern can be ideal for one-off or simple cases.

Mediator

A more robust way to manage events is by inserting a centralized layer to handle dispatching events to the appropriate listeners. This is what the Mediatorwiki pattern (and the Symfony event dispatcher) does.

The point of a Mediator is that it's a centralized transit station for every event in the system, so it needs to be accessible across the entire scope of the application (or the mediated parts, anyway). Note carefully, this does not mean you should treat it as a global variable and access the Mediator willy-nilly with global keywords or wrap it in some sort of evil singleton object or static property/method. This kind of abuse will lead to the problems @liquorvicar posited in the first answer. However, I strongly disagree with that answer's assessment that:

"Having an eventDispatcher that's everywhere in your app and does almost everything can make your code harder to test/understand/maintain etc (it can approach a God object)"

This is only the case if you misuse the Mediator; it should dispatch event notifications and nothing else. I would caution you against extending it like you suggest in option (2) of your question for this reason. When used correctly, a mediator object is exceedingly testable. There's nothing simpler than mocking the behavior of dependency objects specified in your constructors. That's what unit-testing is all about.

Answer

So, if you need non-linear event management in your application I would strongly suggest option (1) from your question. It's perfectly acceptable as long as you don't abuse it. Glossing over the Symfony implementation it appears to support any PHP callable as a listener. Personally, I prefer a system that allows the lazy-instantiation of class-based listeners for a more efficient and object oriented paradigm, but the implementation details are up to you.

The Chain of Responsibility pattern is closely related to the Mediator and is another valid method for accomplishing similar results. If you're interested, I'd suggest the link posted earlier to @ircmaxell's blog post.

这篇关于使用Symfony的EventDispatcher组件的正确方法是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆