簡體   English   中英

什么是使用Symfony的EventDispatcher組件的正確方法?

[英]what's the proper way to use Symfony's EventDispatcher component?

我想通過使某些類可觀察來促進我的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無關,而是與您的問題有關。 如果你只是想要一個答案,你可以跳過(有點)學術討論並跳到最后。

討論

事實 :增加應用程序的大小意味着復雜性的一致性增加。

隨着應用程序范圍的擴大,您會發現自己添加了越來越多的類來實現必需的功能。 突然之間,記住Foo對象在創建Bar對象時需要執行某些特定操作並不容易。 此外,當您的對象開始為彼此提供互補功能時,維持必要的關系變得越來越困難而不會使用非常緊密耦合的對象。

我們需要一種方法來讓對象進行通信,而無需硬編碼顯式引用,當事情發生變化時我們會忘記改變。 那么我們如何在快速增長的對象圖的節點之間管理這種相互關聯的功能呢?

如果你想要它持久,你必須談談

讓我們稍微繞道一下,考慮一個浪漫的比喻......

任何關系都需要一致的溝通,如果它將持續。 當然,你和你的伴侶可以在星期六晚上聚在一起進行陰暗的連接,而不是在一周剩下的時間里互相交談。 然而,這種類型的溝通通常會導致脆弱的關系,在這種關系中,任何一方都不了解對方實際需要在關系的背景下運作良好的東西。

繼續這種類比,隨着你的個性隨着時間的推移而緩慢變化(並且它會),這種缺乏溝通會阻止你的伴侶理解如何最好地與你互動。 最終,所有破碎的承諾和未接來電都成了頭,這種關系再也不起作用了。 它壞了。

您的應用程序以相同的方式工作。 代碼應該足夠成熟,可以說:“嘿寶貝,我可能會改變,但如果我這樣做,我保證我會永遠讓你知道我發生了什么。” 遺憾的是,隨着復雜性的增加,傳統的直線應用程序設計使得這種通信難以在沒有類之間緊密耦合的情

進入活動管理

這就是事件管理的全部意義所在。 目標是為我們的對象提供一種彼此通信的方式,這種方式不會與需要與之通信的對象進行硬編碼關系。 與大多數編程問題一樣,沒有一種單一,具體,“正確”的方法來實現這一點。 你的問題特別提到了兩種可行的方法,所以我會解決這些問題。 如果你想了解其他一些選項,@ ircmaxell最近發布了一篇很好的調查博客文章,關於使PHP應用程序“可插拔”

觀察

實際上,你會發現Observer模式的實際PHP應用程序很少。 這是因為如果您希望您的代碼非常動態,那么在您將觀察者附加到所有地方的主題對象之前不需要很長時間。

當發生這種情況時,你已經開始嘗試實現松散耦合,但是你已經創建了一個不同類型的問題: 手動附加所有觀察者和主體 例如,如果應用程序中的每個類都是Logger觀察者對象的主題,那么您已經為自己創建了大量工作。 此外, 恕我直言這種方法有時會通過移動可能更准確地描述為主題構造函數的方法簽名之外的主題的實際依賴性的東西來模糊您的API。

如果我們使用集中式調度程序在事件發生時通知感興趣的對象,我們的應用程序將更加靈活,盡管Observer模式可以是一次性或簡單情況的理想選擇。

中間人

管理事件的更健壯的方法是插入一個集中層來處理調度事件到適當的偵聽器。 這就是Mediator wiki模式(以及Symfony事件調度程序)所做的事情。

Mediator的重點在於它是系統中每個事件的集中式中轉站,因此需要在整個應用程序范圍內(或者介導的部分)訪問它。 請注意,這並不意味着您應該將其視為全局變量,並使用全局關鍵字無條件地訪問Mediator,或者將其包含在某種邪惡的單例對象或靜態屬性/方法中。 這種濫用將導致@liquorvicar在第一個答案中提出的問題。 但是,我強烈不同意該答案的評估:

“擁有一個eventDispatcher,它在你的應用程序的任何地方,幾乎所有東西都可以使你的代碼更難以測試/理解/維護等(它可以接近上帝的對象)”

只有在您濫用調解員的情況下才會這樣; 它應該發送事件通知而不是其他任何東西。 因為這個原因,我會提醒你不要像你在問題的選項(2)中建議的那樣擴展它。 正確使用時,中介對象極其可測試。 沒有什么比模擬構造函數中指定的依賴項對象的行為更簡單了。 這就是單元測試的全部意義所在。

回答

因此,如果您在應用程序中需要非線性事件管理,我強烈建議您從問題中選擇(1) 只要你不濫用它,這是完全可以接受的。 對Symfony實現進行着色,它似乎支持任何PHP可調用作為監聽器。 就個人而言,我更喜歡一個系統,它允許基於類的偵聽器的惰性實例化,以實現更高效和面向對象的范例,但實現細節由您自己決定。

責任鏈模式與中介密切相關,是實現類似結果的另一種有效方法。 如果您有興趣,我建議先前發布@ ircmaxell博客文章中的鏈接。

我會避免(2)。 繼承可能最常用的模式,可能與此無關。 選項(1)和(3)之間的選擇可能取決於您的上下文。 雖然避免緊耦合是好的,但你應該警惕瑞士軍刀解決方案。 擁有一個eventDispatcher,它在你的應用程序中無處不在,幾乎所有東西都可以讓你的代碼更難以測試/理解/維護等(它可以接近一個God對象)。 另一方面,Spl解決方案更簡單,因此如果您確實需要多個觀察者/可觀察者,您可能會發現您必須維護太多的SplObservers / SplSubjects。

與OOP中的大多數事情一樣,沒有最好的方法,通常取決於您的確切用例......

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM