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.