How To: Implement Event-driven Architecture in Java
jingkecn opened this issue · 0 comments
Introduction
In computing systems, Event-driven Architecture (abbr. EDA,same in the following context) is
a pattern to implement a loosely coupled system.
Inspired by .Net
In .Net, Delegation is the basic pattern to implement EDA, and Event is declared and defined by delegate
keyword and event
keyword:
// 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;
However, an event
is supposed to be dispatched by an event target
and listened by an event listener
.
Event Target
An event target
is in charge of:
- Definition of an event
- Dispatching an event
/**
* 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
An event listener
is in charge of handling event if any invoked.
/**
* A sample of event listener in C#.
**/
class SampleEventListener {
void OnSampleEventInvoked(/* params */) {
// Handle event here.
}
}
Registration and Cancellation
In general, a central controller in global scope is supposed to be in charge of:
- Register an
event listener
to anevent target
. - Cancel an
event listener
from anevent target
.
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;
Implementation in Java
Then here comes the question: what can be things corresponding to delegate
and event
of .Net in Java?
From the observation on previous .Net samples, we can conclude that its delegate
declares and defines a signature of the method handling event. Thus functional interface
in Java is quite close to it.
// Defines an event handler.
@FunctionalInterface
interface ISampleEventHandler {
void invoke(/* params */);
}
Then what can be corresponding to event
?
Still from the observation on previous .Net samples, an object declared by keyword event
has two operators: +=
and -=
, it means that an event
should be a collection. Then the elements of this collection should be defined by the delegate
: SampleEventHandler
// An event in Java can be a collection of event handlers.
HashSet<ISampleEventHandler> sampleEvent = new HashSet();
Then all we have to do is to copy mechanically:
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 and 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);
Conclusion
The most counterintuitive part of implementations suggested by this post lies in:
Lambda Expressions
Lambda expressions were introduced as a new feature in Java 8 to invoke existing methods, so
sampleEventTarget.sampleEvent.add((/* params */) -> { /* ... */ });
is actually a syntax sugar of
sampleEventTarget.sampleEvent.add(new ISampleEventHandler() { /* ... */ });
Method References
Java 8 has also introduced Method References, so that we can get rid of the coerciveness of implementing the functional interface ISampleEventHandler
, instead, we can register directly a method as an event handler
as long as it has the same signature as defined in ISampleEventHandler
.
[!TODO]
Android has recently supported Kotlin, EDA implementation in Kotlin should be much closer to .Net pattern and should also be much more comprehensive.
See Also
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. |