Over the course of time software development has changed greatly. The first major change was the introduction of structured programming. With structured programming an application was broken into many functions, with each function ideally performing one small task. Each function was named for the task it should perform. The seismic shift came with Object Oriented programming; made popular with Smalltalk and then commercially with C++. Now developers wrote their code in terms of Classes; with each class modeling something from the real world.
One of the primary aims of both these approaches was to help communication both within and across teams; it’s much easier for a developer to talk to other developers and end users when she can say ‘How exactly does a Customer open an Account?’ and then model that in terms of classes.
The last few years has seen the rise of ‘patterns’ in the developer community. Patterns are all about communication; they help developers understand and describe much more clearly the intent of a given piece of code.
Patterns and Communication
Imagine two carpenters talking; they are discussing the type of joint to use for a set of drawers they are making. One asks:
“What sort of joint are you going to make for the drawer?”
And the other replies:
“I’ll take the wood, cut down about 2 centimetres; cut back up at a forty-five degree angle; cut down 4 centimetres then back up at forty-five degrees; then down again and up at forty-five degrees; then...”
What the second carpenter is discussing is how to cut a dovetail joint, a standard type of joint in woodworking. Describing the joint in this way does not aid communication; in fact it hinders communication. If the second carpenter said “I’m going to use a dovetail joint” or “I’m going to use a mortise and tenon joint” that communicates far more information in far fewer words.
In the second case the carpenters are describing common joints and can make a decision based on their understanding of the joints, such as the cost versus complexity of each joint; the strength of each joint; and the suitability of each joint to the job.
This is where patterns come into play in software development. If a developer can say “I’m using the Strategy pattern” or “I’m using the Factory Method pattern”, this conveys lots of meaning to another developer who understands the patterns and how they are used.
What is a Pattern?
A pattern does not describe code; instead it allows developers to communicate the mechanism by which the problem they are discussing can be solved. The pattern is a re-usable solution to a common design problem. Amongst other things patterns have a name and an intent. Both of these are extremely important.
Many patterns look alike, and sometimes examining the UML diagram of the pattern is not enough to tell them apart, for example the Proxy and Decorator patterns share the same UML diagram but the intent of each is different. One of the key concepts to grasp when studying patterns is the intent of the pattern; that is why and when a particular should be used.
Naming is also very important. Many people call all code that creates something a factory. However not everything that is a creator is a factory. Loosely calling something a factory does not convey the correct information. In fact there are two distinct factory patterns, the Factory Method and the Abstract Factory. Understanding the distinction between these patterns and understanding that not all creators are factories allows developers to communicate clearly just based on the name of the pattern.
Good OO design follows certain principles, and using patterns supports these principles. For example, to allow a greater degree of flexibility developers should code to an interface rather than an implementation, where interface in this case does not necessarily mean the ‘interface’ keyword but can also mean an abstract class. Many design patterns follow the ‘open/closed’ principle, open for extension but closed for modification. So whilst the tested code physically should not change, it should be written to except new pieces of code. The strategy pattern for example allows the supplying of code at runtime, the .NET Array.Sort class is an example of this. The Sort algorithm cannot physically be modified but you can supply the algorithm the code for it to use to compare items. Thus the code is closed for modification but it can be extended to sort different types of entities.
Patterns in .NET
Much of the technical literature around patterns is written for a particular audience, the Java developer. Java is very similar to .NET and developers can often take a pattern written in Java and port that pattern to C# or VB.NET. However, Java and .NET are not the same environment, even though they have the same roots. For example, .NET has delegates, Java does not; .NET (at least in C#) has anonymous methods and Java does not. Both of these related technologies can be applied to many patterns, which make the way many patterns are written in C# fundamentally different to the way they are written in Java. Understanding how to use C# features is a key to developing and using patterns in .NET to their full extent.
Using these .NET features changes the way we use some common patterns. For example, using delegates makes writing the Observer pattern trivial; using partial classes with the State pattern allows us to split the code across multiple files, with each file containing one class per state, making it easier to manage the code; using ThreadStatic means that thread bound singletons can be created easily. Other techniques that can be used in patterns are .NET iterators and transparent proxies.
Patterns provide a mechanism for communication between developers. They allow developers to communicate at a higher level than code and so aid in the understanding of the code being developed. Patterns can be adapted to the environment in which they are used, and in particular can take advantage of many of the features of .NET including delegates, partial classes and anonymous methods.