his is the last in a series of three articles that explore the built-in support for standard design patterns in ASP.NET, and ways in which you can implement common patterns in your own applications
The patterns discussed so far in the first two articles in this series (Article 1
, Article 2
) relate to features that are either automatically implemented by ASP.NET, or are relatively easy to implement as custom features of an application. But those cover only a small proportion of the total number of design patterns, although they include those that are most useful and most commonly implemented within web applications.
The following sections discuss more advanced design patterns typically seen in Windows Forms and other desktop applications, but less often suited for implementation within web applications. However, the overall aims of these patternsparticularly the Command and Event patternsare laudable, and it is possible to provide a useful modified implementation for ASP.NET applications. See the sidebar "Useful Resources" for a full list of references to patterns and architectural guidance.
The Factory, Builder, and Injection Patterns
The Factory, Builder, and Injection design patterns specify various ways to separate the construction of a complex object from its representation, letting you use the same construction process to create different representations. These patterns are commonly associated with the instantiation of objects from classes, and control the way that the application receives such instances.
In the Factory pattern, subclasses of the object generator determine the object type. An example of the Factory pattern is the DbProviderFactory class in .NET, which generates instances of Connection, Command, and DataAdapter objects specific to a particular provider (such as SqlClient or OleDb) depending on the provider name you specify when you create the DbProviderFactory instance. Other .NET classes, such as XmlReader and XmlWriter, provide a static Create method that returns a pre-configured instance of the specified class.
|Figure 1. The Command Pattern: The Command pattern separates the command invoker from the command receiver, letting developers add and alter actions and commands easily. It also enables a command to initiate multiple actions.|
In the Builder and Injection patterns, the client passes instructions or hints to the object generator to specify the required object type. In some implementations, the client can use attributes to specify the class types for injection into the current context. The object builder can set properties and execute methods on the object before returning it. It can also avoid construction overhead, and return an existing instance of an object when appropriate instead of creating a new instance. The Microsoft patterns & practices
group provides a utility called ObjectBuilder that incorporates these features as part of the CAB and other software factories.
The Command Pattern
The Command pattern separates the command invoker from the command receiver, allowing developers to add commands and actions to an application without having to relate them directly. This pattern also allows a command to initiate multiple actions (see Figure 1).
The Command pattern is particularly useful in applications that load modules at runtime to extend their functionality. These modules might load in response to user interaction, or because of specific application configuration. As they load, each module can add commands to the application shell, extend the application by adding new actions, and relate the new and existing commands to new and existing actions.
The Observer and Publish-Subscribe Patterns
|Figure 2. The Observer and Publish-Subscribe Patterns: Similar to the Command pattern, but targeted toward application events, these patterns separate event creation from event consumers.|
The Observer and Publish-Subscribe patterns separate the creator of an event from the receiver(s), in much the same way as the Command pattern separates commands from actions. The main difference is that the events and actions may not relate to user commands, but to occurrences of specific events within the application (see Figure 2
). For example, a data source provider may raise events when it connects to a database and executes a query. Other classes can subscribe to receive this event, allowing them to interact with the source to provide information, change the behavior, or even cancel the process.
The Observer pattern requires that the client or object wishing to receive notifications (the Observer) subscribe this interest to the object that fires the event (the Subject).
The Publish-Subscribe pattern uses an "event channel" that sits between the client or object wishing to receive notifications (the Subscriber) and the object that fires the event (the Publisher). This channel can be the .NET event system, allowing code to create application-specific events that pass a custom "arguments" instance containing values required by the subscriber. The purpose is to avoid dependencies between the Subscriber and the Publisher; thereforeunlike the standard Observer patternallowing any Subscriber that implements the appropriate event handler to register for and receive events generated by the Publisher.
Using Commands and Events in ASP.NET
One of the major issues with many standard design patterns when applied in ASP.NET is the difficulty in successfully using events. In a Windows Forms or executable application, an object or window can subscribe to events, and receive these events when they occurat any time during the lifetime of that object or window. This lifetime can be the same as the main application, meaning objects can raise events at any time and be sure that the subscribers will be active and receive them.
In ASP.NET, however, the "one shot" nature of HTTP and the web page display mechanism means that as soon as the server has finished generating a page and rendering it to the client, it destroys all the objects it used in that page. Therefore, it's pointless to have server-based components instantiated within an ASP.NET page subscribe to events that may occur when the user requests the next page, or in the period between requests.
For example, in the MVP pattern, the data access layer that implements the Model may raise an event indicating that the underlying data has changed. However, in ASP.NET, the code that generated the View is no longer available and so cannot receive this event. There are ways to implement objects that run as a background service to detect events, and some of the more recent client-side technologies (such as AJAX) attempt to extend the event handling capabilities of web pages using asynchronous background requests. However, these do not provide the same level of event support as in a Windows Forms or executable application.
Of course, events are useful in ASP.NET when all the participating objects are loaded and available within the same page. This approach allows objects to expose events without prior knowledge of the classes and objects that might subscribe to these events, as described in the Publish-Subscribe pattern.
The example application demonstrates both the Publish-Subscribe pattern and a modified Observer pattern that provides a technique for executing methods (effectively, activating commands) on subscriber/observer objects that do not directly depend on the publisher/subject. Both examples implement a fictitious car production line and associated departments such as parts, sales, and transport.
In this example scenario, a client ASPX page creates an instance of the publisher (the production line) and instances of any subscribers (parts, sales, or transport departments) that should receive notification when the production line builds a new car. Each subscriber maintains a count of the number of notifications received, based on the model name of the car. The following sections of this article describe these two implementations.