Consider the Modem hierarchy in Figure 35-1. The Modem interface contains the generic methods that all modems can implement. Three derivatives are shown: one that drives a Hayes modem, one that drives a Zoom modem, and one that drives the modem card produced by Ernie, one of our hardware engineers. How can we configure these modems for UNIX without putting the ConfigureForUnix method in the Modem interface? We can use a technique called dual dispatch, the mechanism at the heart of the VISITOR pattern. Figure 35-1. Modem hierarchy
Figure 35-2 shows the VISITOR structure, and Listings 35-1 through 35-5 show the corresponding C# code. Listing 35-6 shows the test code that both verifies that VISITOR works and demonstrates how another programmer should use it. Figure 35-2. VisitorNote that the visitor hierarchy has a method in for every derivative of the visited (Modem) hierarchy. This is a kind of 90° rotation: from derivatives to methods. The test code shows that to configure a modem for UNIX, a programmer creates an instance of the UnixModemConfigurator class and passes it to the Accept function of the Modem. The appropriate Modem derivative will then call Visit(this) on Modem-Visitor, the base class of UnixModemConfigurator. If that derivative is a Hayes, Visit(this) will call public void Visit(Hayes), which will deploy to the public void Visit(Hayes) function in UnixModemConfigurator, which then configures the Hayes modem for Unix. Listing 35-1. Modem.cs
Listing 35-2. HayesModem.cs
Listing 35-3. ZoomModem.cs
Listing 35-4. ErnieModem.cs
Listing 35-5. UnixModemConfigurator.cs
Listing 35-6. ModemVisitorTest.cs
Having built this structure, new operating system configuration functions can be added by adding new derivatives of ModemVisitor without altering the Modem hierarchy in any way. So the VISITOR pattern substitutes derivatives of ModemVisitor for methods in the Modem hierarchy. This dual dispatch involves two polymorphic dispatches. The first is the Accept function, which resolves the type of the object that Accept is called on. The second dispatchthe Visit method called from the resolved Accept methodresolves to the particular function to be executed. The two dispatches of VISITOR form a matrix of functions. In our modem example, one axis of the matrix is the various types of modems; the other axis, the various types of operating systems. Every cell in this matrix is filled in with a function that describes how to initialize the particular modem for the particular operating system. VISITOR is fast. It requires only two polymorphic dispatches, regardless of the breadth or depth of the visited hierarchy. |