How Notifications Travel
The most well-known delivery mechanism for sending notifications from one part to another uses indirect method calls, but there are other mechanisms. With indirect method calls, there are essentially two different ways to deliver notifications:
- Typed calls. A typed call is a call through a known type (a class or interface). The caller has a pointer to a typed object, and calls one of the object's methods.
- Untyped calls. An untyped call is a call to a method of an object whose type is unknown. The caller knows only the signature of the method to be called, and knows nothing about the object to which the method belongs.
Languages such as Java and C++ natively support typed calls. Languages such as C#, VB.NET, and Delphi support both typed and untyped calls. Java programmers can also use untyped calls, but need to resort to reflection and Method.invoke() to do so.
In Figure 1, assume you have two classes A and B, which interact using notifications. When some event occurs in A, you want B.DoSomething() to be executed. To achieve this, A will have to send a notification to B. To send notifications using typed calls, class A needs to contain a field referencing type B. At runtime, this reference is somehow set; for this example, it doesn't matter how or when. When firing the event, class A invokes the method B.DoSomething() on the referenced object using code of the form:
if (referenceToB != null) referenceToB.DoSomething();
To send notifications using untyped calls, class A needs to have a field referencing not type B, but a method with the signature of B.DoSomething(). In .NET languages, delegates are language-defined entities that can point to methods with a given signature. In C#, class A might have a delegate defined like this:
public delegate void SomeHandler();
The delegate can then be used to define a field to hold a reference to a method with the given signature:
public event SomeHandler myHandler;
At runtime, myHandler would be initialized somehow (the details of which are not important here) to point at the method DoSomething of some instance of class B. Class A would then use this code to fire an event:
if (myListener != null) myListener();
In Java, the same thing can be accomplished using reflection with Method.invoke(). Class A would need to have a field of type Method. At runtime, class A would need to initialize the Method field to point to method DoSomething() of some instance of B. Class A would then use the Method field to invoke B.DoSomething().
Typed and untyped calls achieve the same net result (invoking DoSomething() on an instance of class B), but with important differenceswith typed calls, class A must know about type B, so A must be created with the knowledge of B. Also, any objects that wish to handle notifications from A need to be of type B, or derived from B. With untyped calls, A does need to know about type B. Objects wishing to handle notifications from A don't need to be of any special type. How important this distinction is usually depends on the size of the system you're building, and whether you have control of both the sender and receiver of notifications.