Event driven processing is found in almost every user interface, communication subsystem, and is pervasive in the design of operating systems.
A model of window event handling for the Windows operating system is shown in the diagram, below.
-
Windows Event Processing -
.Net Delegate Structure
Let's identify code that has events to publish as "Publisher" packages, and code that wants to react to those events as "Subscriber" packages. The Publisher creates instance of a class derived from MulticastDelegate and exposes it as a public resource. Its purpose is to hold references to functions called when a specific event occurs. Here, the event is just something that happend in the application code that the designer thinks of as significant. The Publisher part of the code that generates the event invokes its delegate to, in turn, invoke function references it holds to subscriber's event handlers. These references came from subscriber code, during its initialization. This apparatus decouples the Publisher and Subscriber. The Publisher doesn't need to know anything about the Subscriber or its event handlers, and the Subscriber doesn't need to know anything about how the event got generated in the Publisher. It only knows that the Publisher exposes a delegate that it promises to call when the event occurs. -
Event Triggers Invocation of Invokers Commands
The Command Pattern generalizes the notion of a delegate. Actually the Command Pattern was invented first, and oddly, .Net decided to use a less flexible delegate structure. The Command Pattern is composed of an Invoker type that plays the same role as the .Net Delegate. However, it binds to a command interface that can be implemented by derived classes to call global or member functions of any desired signature. Since the invoker binds to the interface, the invoker can hold a mix of derived command types and hence call any type of handler. The derived concrete command objects are responsible for acquiring any data necessary to pass to the event handlers in its receiver. Think of the library code as Publisher and the client code as subscriber.
Conclusions for Delegation and Events:
One important design goal for event handling is to decouple event publishers from event subscribers.
- .Net Delegates allow the publisher to be entirely ignorant of the subscribers. It doesn't need to know how many nor their types. Subscribers need to know the publisher and its delegate types, but don't need to know any details of how the event generation is implemented. Because of the way Delegates are implemented, each Delegate type can hold references to functions with only one specified signature, but that function can be a member of any type.
- Command Pattern event handling also decouples Publisher and Subscriber, but in a slightly more flexible way than the .Net Delegate. Since the concreteCommand objects derive from command and are implemented by the Subscriber, their execute methods can hold references to functions of any signature, and the invoker can hold any mixture of concreteCommand objects needed for the application.