There are many areas where design patterns can be applied to simplify the development of .NET applications. And many design patterns have already been described and categorized by software engineers. This article touches on a few of the more common design patterns and describes ways in which they can be used to simplify the .NET Framework Class Library, and touches on some of the most useful design patterns used by the .NET Platform.
Sometimes Too Much of a Good Thing Can Make Things Difficult
One of the primary benefits of .NET is that it provides software developers with a uniform framework that makes it easier to build enterprise-level applications. The Framework Class Library provides access to lower-level system functionality and is designed to be the foundation on which all .NET applications are built. The Framework Class Library provides a comprehensive set of concrete and abstract classes that either can be used directly or to build specific types that derive their definition from .NET class interfaces. This high degree of flexibility can potentially lead to situations in which the user’s types are tightly coupled to the .NET class namespaces. As .NET evolves and extends to other platforms, tight coupling will lead to applications that are difficult to maintain and adapt to changing requirements. Therefore, it becomes important to carefully consider ways of designing .NET applications that are scalable and rugged.
Benefits of Design Patterns
A popular approach to building scalable, extensible, and reusable systems is through the use of Design Patterns. Design Patterns are time-tested solutions to recurring software design requirements. They are given names and other properties, and are expressed as a commonly recurring class structure used to solve specific design problems. Their use encourages software developers to build systems that incorporate the good software engineering principles of low coupling and high cohesion.
Design Patterns are ideally suited to developing .NET applications because the .NET platform is fully object oriented. In .NET, everything is an object. Furthermore, when you consider the mind-boggling breadth of functionality and the number of classes provided by the .NET Framework, it becomes clear that a consistent approach to developing .NET applications based on best practices is necessary.
If you examine the .NET Framework Documentation, you will see the namespaces are categorized into 12 different groups. Some have specific responsibilities, such as “Reflection” and “.NET Framework Security,” while others are given more general names, such as “Common Tasks” and “Framework Services.” Within each namespace there are a number of concrete and abstract classes that provide many ways to accomplish a given task. To illustrate, consider the “System Collections” namespace. This namespace exposes a large number of general-purpose classes, as well as specialized classes, that you can use for your own types. For example, you can create your own collection classes by inheriting from one of the many .NET Framework collection classes and then adding your own code to implement your custom functionality. The problem with this approach is that your custom types are exposed to the underlying .NET system. Any changes made to .NET (bug fixes, new features, etc.) will propagate to your classes. This will increase the maintenance overhead of the application. The Design Pattern approach would be to:
- Identify the source of variability.
- Design a class structure that isolates this source for your application.
So, going back to the example given above, you could create a simpler interface to the System.Collection namespace and give it all the functionality you need. All your custom types would derive from this interface, which you could define as necessary.
This “indirection” layer gives you a less coupled and more cohesive way to create custom collections. More importantly, you have effectively isolated that particular namespace. This is an example of a design pattern called the “Facade Pattern,” whose intent is to isolate a subsystem from a client by placing the subsystem behind a simpler interface which exposes only the functionality that client needs. The Facade Pattern can be used to create simpler interfaces for any of the .NET namespaces. Other structural patterns have been described to solve specific structural design issues. The Adapter Pattern is intended to provide a class that converts an incompatible interface to one that a client expects. Adapters would be useful for creating custom types that derive from the System.XML namespace. This way, the XML emitted from the custom type can be customized to the needs of a particular client.
Another useful pattern is the Proxy Pattern. The intent of the Proxy Pattern is to provide a surrogate object for another object in order to control access to it. For example, the surrogate object can be designed to cache frequently accessed, but static, data locally to minimize access to a remote server object. Several of the classes in the System.Runtime. Remoting namespace provide support for creating proxy objects and serializing their representation.
In addition to structural patterns, behavioral patterns have also been described to solve design problems relating to application behavior. The .NET platform makes extensive use of design patterns to promote loose coupling between classes. For example, Delegates use the Observer Pattern to provide a callback mechanism whereby an object that raises an event can invoke method calls on event handling objects without the client’s knowledge. In this model, the event sender declares an object called a “Delegate.” Any classes wishing to be notified of this event will simply provide a method with the same signature as the Delegate. This method would then respond to the event. This approach allows you to easily add additional event receivers without the need to modify the event sender code. All you need to do is create a method with the same signature as the Delegate and respond to the event. The delegate provides a level of indirection by keeping the event sender from knowing which object will receive the event.
This mechanism can be further enhanced using the Chain of Responsibility Pattern. This design pattern describes an approach to avoid coupling between the sender and recipient of a message while allowing more than one object to have a chance to respond to the message. In .NET, this pattern can be implemented using Multicast Delegates. A multicast delegate differs from a regular delegate in that it references to more than one method. When the multicast delegate is invoked, the methods are executed synchronously in the order in which they appear. Thus, you can pass an event to a chain of objects and give any one of them a chance to respond. This pattern is very useful for maintaining event handlers for menu items and other interface controls.
Another useful pattern is the Command Pattern. This pattern describes a way to encapsulate a request as an object. This allows you to parameterize the request and pass it on as an object. .NET incorporates the intent of the Command Pattern by providing the System.EventsArgs. Your custom types would derive from this class so you can define custom data associated with events.
In conclusion, many developers will be approaching .NET from a background in Visual Basic, which lacks many of the features of an Object Oriented Language. Design Patterns give these developers a way to build .NET applications while incorporating good software engineering principles.
James Maioriello, Ph.D., is a Senior Design Engineer for Formation Systems Inc., a corporation located in Massachusetts dedicated to the development of software systems for product development in the processing industries. His position allows him to use his expertise and skills to design solutions for chemists and other scientists so they can focus their talents on innovation. Dr. Maioriello obtained degrees in Chemistry from Lehigh University and in Software Engineering from Polytechnic University in New York.