jingkecn/blog

如何在 Java 中實現「事件驅動架構」

jingkecn opened this issue · 0 comments

背景

在軟件開發中,「事件驅動架構(簡稱 EDA,下同)」是實現松耦合系統設計的其中一種模式。

源於 .Net 的靈感

在 .Net 中,「委託(Delegation)」是實現 EDA 的基本模式,其「事件(Event)」通過 delegate 關鍵字和 event 關鍵字 聲明定義:

// A delegate is a type that represents references to methods 
// with a particular parameter list and return type.
delegate void SampleEventHandler(/* params */);

// The event keyword is used to declare an event in a publisher class.
event SampleEventHandler SampleEvent;

而「事件」則由「事件監聽目標(Event Target)」觸發(Dispatch),并由「事件監聽器(Event Listener)」監聽(Listen)。

事件監聽目標(Event Target)

一個「事件監聽目標」通常負責:

  • 定義事件(Event Definition)
  • 觸發事件(Event Dispatching)
/**
 * A sample of event target in C#.
 **/
class SampleEventTarget {
    // Event definition.
    delegate void SampleEventHandler(/* params */);
    event SampleEventHandler SampleEvent;

    // Event dispatching.
    // Dispatch SampleEvent somewhere in member methods.
    //     SampleEvent?.invoke(/* params */);
}

事件監聽器(Event Listener)

一個「事件監聽器」則負責在監聽到事件時處理(Handle)事件。

/**
 * A sample of event listener in C#.
 **/
class SampleEventListener {
    void OnSampleEventInvoked(/* params */) {
        // Handle event here.
    }
}

註冊(Registration)和註銷(Cancellation)

通常來說,在全局作用域,中控負責:

  • 向事件目標「註冊」事件監聽器
  • 向事件目標「註銷」事件監聽器
var sampleEventTarget = new SampleEventTarget();
var sampleEventListener = new SampleEventListener();

del lambdaEventHandler = (/* params */) => {
    sampleEventListener.OnSampleEventInvoked(/* params */);
};
// Register lambda event handler to event target.
sampleEventTarget.SampleEvent += lambdaEventHandler;
// Cancel lambda event handler from event target.
sampleEventTarget.SampleEvent -= lambdaEventHandler;

// Register event handler reference to event target.
sampleEventTarget.SampleEvent += sampleEventListener.OnSampleEventInvoked;
// Cancel event handler reference from event target.
sampleEventTarget.SampleEvent -= sampleEventListener.OnSampleEventInvoked;

在 Java 中實現

那麼在 Java 中,與 .Net 中 delegateevent 相對應的又是什麼呢?
綜上可知,delegate 聲明定義了事件處理方法的函數簽名,那麼這對應了 Java 中的函數式接口(Functional Interface):

// Defines an event handler.
@FunctionalInterface
interface ISampleEventHandler {
    void invoke(/* params */);
}

那麼 event 呢?經觀察,我們可推定,.Net 中的 event 聲明了一個集合(由 +=-= 操作符推測),而其元素為 SampleEventHandler 委託類型:

// An event in Java can be a collection of event handlers.
HashSet<ISampleEventHandler> sampleEvent = new HashSet();

那麼,接下來我們只需要依樣畫葫蘆就好了:

事件監聽目標(Event Target)

/**
 * A sample of event target in Java.
 **/
class SampleEventTarget {
    // Defines an event handler.
    @FunctionalInterface
    interface ISampleEventHandler {
        void invoke(/* params */);
    }

    // An event in Java can be a collection of event handlers.
    HashSet<ISampleEventHandler> sampleEvent = new HashSet();

    // Event dispatching.
    // Dispatch sampleEvent somewhere in member methods.
    //     sampleEvent.forEach { handler -> handler.invoke(/* params */); };
}

事件監聽器(Event Listener)

/**
 * A sample of event listener in Java.
 **/
class SampleEventListener {
    void onSampleEventInvoked(/* params */) {
        // Handle event here.
    }
}

註冊(Registration)和註銷(Cancellation)

SampleEventTarget sampleEventTarget = new SampleEventTarget();
SampleEventListener sampleEventListener = new SampleEventListener();

ISampleEventHandler sampleEventHandler = new ISampleEventHandler() {
    @Override
    void invoke(/* params */) {
        sampleEventListener.onSampleEventInvoked(/* params */)
    }
};
// Register event handler to event target.
sampleEventTarget.sampleEvent.add(sampleEventHandler);
// Cancel event handler from event target.
sampleEventTarget.sampleEvent.remove(sampleEventHandler);

Function</* param types */, Void> lambdaEventHandler = (/* params */) => {
    sampleEventListener.onSampleEventInvoked(/* params */);
    return null;
};
// Register lambda event handler to event target (Java 8+ required).
sampleEventTarget.sampleEvent.add(lambdaEventHandler);
// Cancel lambda event handler from event target.
sampleEventTarget.sampleEvent.remove(lambdaEventHandler);

// Register event handler reference to event target (Java 8+ required).
sampleEventTarget.SampleEvent.add(sampleEventListener::OnSampleEventInvoked);
// Cancel event handler reference from event target.
sampleEventTarget.SampleEvent.remove(sampleEventListener::OnSampleEventInvoked);

總結

本篇實現的思路中最反直覺的地方有兩處:

Lambda 表達式

Java 8 新增了 lambda 表達式以觸發現有的方法,因此上面 Java 中的

sampleEventTarget.sampleEvent.add((/* params */) -> { /* ... */ });

實際上是

sampleEventTarget.sampleEvent.add(new ISampleEventHandler() { /* ... */ });

的語法糖。

方法引用(Method References)

Java 8 中還新增了「方法引用(Method References)」,因此我們完全可以擺脫 ISampleEventHandler 實現的強制性,直接註冊與 ISampleEventHandler 有相同函數簽名的方法即可。

[!TODO]
Android 新增了 Kotlin 支持,在 Kotlin 中實現相同的「事件驅動架構」會更加接近 .Net 的模式。

參見

No. Link
[1] Wikipedia - Event-driven Architecture
[2] How to: Publish Events that Conform to .NET Framework Guidelines (C# Programming Guide).
[3] The Java™ Tutorials > Method References.