As we said, SOLID principles are beyond any specific consideration on how to resolve a certain coding problem and even beyond languages or paradigms. However, before Robert Martin defined these principles, other patterns were already in use related to very distinct aspects of coding and structuring applications.
In real life, a class can use one or more patterns, making it diffuse the boundary between the two. Additionally, you can begin to use a simple pattern and evolve into other more complex patterns depending on the needs of our application.
In 1995, Eric Gamma, Richard Helm, Ralph Johnson, and John Vlissides (since then, the Gang of Four or GoF for short) published a book that has remained a reference point: Design Patterns: Elements of Reusable Object-Oriented Software.
The authors analyze a total of 23 design patterns applicable in different coding scenarios in order to solve different coding problems.
They divide the 23 patterns into three categories:
Obviously, all these patterns are a lot to be covered in this chapter, even in a superficial way, but we're going to focus on the most frequently used ones and explain their advantages and programming in C#:
The .NET framework itself contains, among others, these patterns: Singleton, Strategy, Factory, Builder, Decorator, and several other patterns in different namespaces.
There are numerous statistic reports on the Internet about the GoF pattern's usage. Obviously, it's not a question of using this or that pattern because of general acceptance. On the contrary, the reasons to use them are based on the benefits these patterns offer in order to improve the quality of an application.
That said, I'll just review some of them to give you an idea about their possibilities of solving specific problems. However, an agreement seems to exist when placing the following eight patterns among the most used:
Note that some patterns, such as Iterator, are not included here just because they're already present in the vast majority of the collection's libraries (such as in the System.Collections
and System.Collections.Generic
namespaces in .NET). Another typical case is Abstract Factory, which is widely used in ADO.NET.
Let's start with the most common (and reviled) of them all: Singleton.
The Singleton pattern prevents the creation of more than one instance of a class. It's the most popular pattern because its implementation is required in a great variety of situations and many different languages (also in non-compiled languages, such as JavaScript).
At the same time, it's one of the most reviled because of general abuse of the pattern in many situations in which other patterns would be preferred or even no pattern is required at all (not to mention the difficulties that sometimes arise when including it in unit tests).
The way it should be coded requires the following:
To apply this pattern in our sample, we can imagine a new requisite: for instance, imagine that the user interface requires that either from the current main window or from other future windows, some user information showing the name of the user and the date/time at which the car is selected is available.
The shape of the new class should reflect the pattern and the values required:
public class UserInfoSingleton { // A static variable for the instance, requires a lambda function, // since the constructor is private. private static readonly Lazy<UserInfoSingleton> instance = new Lazy<UserInfoSingleton>(() =>newUserInfoSingleton()); // Private Constructor to avoid direct instantiation private UserInfoSingleton() { UserName = System.Environment.UserName; CarBuyingTime = DateTime.Now; } // Property to access the instance public static UserInfoSingleton Instance { get { return instance.Value; } } private string UserName { get; } private DateTime CarBuyingTime { get; } }
Observe that this class is only for reading purposes, with no meaningful functionality. However, having it instantiated in this manner, no possible duplication is possible. There will always be a unique set of user information.
The class' instance is stored in the private static instance
variable, and the constructor is private in order to avoid external instantiation. Actually, all members except the Instance
property are private.
The other aspect of the class that you might wonder about is the Lazy<UserInfoSingleton>
type of the instance
member, which guarantees that the instance is thread-safe since it won't really be instantiated until it is used by a client of the class.
Wikipedia's definition of the Factory pattern states that, a Factory is actually a creator of objects which have a common interface, without exposing the instantiation logic.
Actually, this is what we did in the last modification in our sample, when we detached the instantiation into a CarFactory
class.
With these changes, we divided the structure of the resulting objects into two parts:
CarFactory
class decides the state structure of the resulting object depending on the brand field (remember that the state of a class is defined by the set of values that its properties hold in a given instant of its execution).SportsCar
and SportsCarWithN
are implementations of a behavior. Each one implements distinct behaviors with respect to the instant Speed
value, and both share the same state structure (same field names and types).In our sample, there is a dependency between the fields, since MaxSpeed
and Photo
directly depend on Brand
, so they should be resolved at construction time. Generally speaking, when there aren't any dependencies of this type, the structure can be more flexible.
The Adapter pattern is one of the most versatile, and it's intended to allow two components that were not originally designed to work together in order to integrate them in the cleanest way possible.
It is, therefore, especially suitable when we have to deal with legacy code, in which it is quite difficult, if not impossible, to modify fragments of the code, but we have the requirement to include new functionality.
The following schema shows the most common way to visually prototype the indirect path that the Adapter pattern implements in order to achieve this goal:
As you can see in the schema, there is a client that uses a certain interface. When the original class needs to change or extend some behavior with minimal or no changes, Adapter is one of the most accepted solutions.
Imagine that we have a class that lists all car brands and that we cannot modify, with the following code:
class ShoppingCarsPortal { static void Main(string[] args) { Console.Title = "Demo of the Adapter Pattern"; ITarget adapter = new VendorAdapter(); foreach (string brand in adapter.GetCars()) { Console.WriteLine("Brand: " + brand); } Console.ReadLine(); } }
On the other hand, a new class has to be used in order to get the list of the cars still calling to the same adapter.GetCars()
function. This class, named ListOfCarsProvider
, holds a method called GetListOfCars
:
public class ListOfCarsProvider { public List<string> GetListOfCars() { List<string> carsList = newList<string>(); carsList.Add("Ferrari"); carsList.Add("Mercedes"); carsList.Add("BMW"); carsList.Add("Ford"); return carsList; } }
We can define a simple interface (ITarget
), which defines the same method signature that the final class requires:
interface ITarget { List<string> GetCars(); }
The next step is to make VendorAdapter
implement ITarget
. The trick is that we make the implementation of GetCars()
call the new list of cars in ListOfCarsProvider
:
class VendorAdapter : ITarget { public List<string> GetCars() { ListOfCarsProvider adaptee = new ListOfCarsProvider(); return adaptee.GetListOfCars(); } }
As you can see, we preserve the functionality of the base class but allow a new way to obtain the list of available cars. We provide a level of indirection with minimal changes.
Another useful (and quite used) pattern in many circumstances is the Façade pattern. It's quite simple, and its main purpose is to unify processes dispersed in distinct functions of a library, giving access to them through a more simple, concrete set of methods.
Wikipedia collects some typical usages of this pattern, for example, when you want to do the following:
The graphic schema that represents this structure is usually represented in this manner:
This means that the façade its just another layer between a set of classes that can either be saved inside a larger library or disaggregated along distinct files. In any case, the pattern allows the unification of that functionality included in the assemblies.
No interface implementation is required since it's only a matter of calling the required methods, thus providing the business logic. You'll find the source code corresponding to the preceding schema in the demo called PatternFacade
. The runtime doesn't have much interest in this since you can easily deduct how it works.
The Decorator pattern is frequently used when you need to add a functionality to a procedure (often obtaining extra data to accompany the standard implementation) but you have to keep the current behavior as it is and only add the new functionality for certain scenarios.
If you think about it, this pattern enforces the application of the Open/Closed principle. The main code remains intact, but the pattern allows the functionality to grow in a controlled manner.
The situation is similar to the first implementation of our SportsClassWithN
type of cars in our sample. The current functionality of the main class (SportsCar
) is not to be changed. But some extra requirements would be needed in case the brand were a Mercedes (and later on, for the Ford brand as well). In this case, the new class inherited from the base class and added some behavior:
Finally, the user interface decides which class is to be implemented at runtime, and in the case of the exception, it instructs the event handler to manage that exception.
The pattern admits (like most of them) slight variations in the way you implement it.
The Command pattern is one of the most used patterns in the behavioral category. GoF authors define the pattern in this way: encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.
As always, a level of indirection is provided. Instead of immediately executing a call, we are allowed to queue it, which has many advantages in a bunch of scenarios, especially now, when more and more implementations require asynchronous processing.
The key part of this pattern is that it decouples the object that invokes an operation from the one that has the knowledge to perform it.
A typical example that GoF authors state as canonical is about the menu's implementation in a classic user interface: in this case, you can implement several user interface artifacts to perform the same action (such as MenuItem
and a toolbar's button) by having both of them perform the same action, that is, making both implement the same concrete Command
subclass.
Again, we can find the Command pattern implemented in a variety of places within .NET Framework. Perhaps one of the most evident patterns is something as simple as the procedure to close a window: you can do it in four different ways:
this.close()
in some of the window's codeAll of the preceding ways provoke a command invocation, sending a WM_CLOSE
message to the window.
You can check the whole list of window messages that a window can handle at the dedicated site, Platform Invoke (http://www.pinvoke.net/).
Invoking this.close()
in a window is a command invocation itself. The .NET Framework also sends one of these messages to be managed by the message's dispatcher window function.
You can intervene in this call thanks to the implementation of the Window.FormClosing
event, which carries information about the command to be executed and allows it to cancel it by assigning the value of the e.Cancel
property (EventArgs
)to true
.
Besides, you can find out the reason for this event to be launched, examining the e.CloseReason
property that the e
argument holds.
These two possibilities are available thanks to the implementation of the command pattern in the internal mechanism used to send WM_CLOSE
messages to a window inside .NET Framework.
The following capture resumes this window-closing scenario in the user interface of our initial demos:
Again, we find another popular pattern that's widely implemented inside the .NET framework for distinct scenarios.
The MSDN documentation states that this pattern enables a subscriber to register with and receive notifications from a provider. It is suitable for any scenario that requires push-based notification.
A typical case of this pattern is implemented in order to link data in a model with the user interface, which shows it in distinct controls: DataGridViews, TextBoxes, and so on. When the user performs an action that implies a modification in the data shown in the UI—such as an update, a deletion, or a modification—the desired behavior is that these controls are automatically informed of the changes and they can update them in the UI.
This source suggests the steps to implement this pattern in .NET:
IObservable<T>
interface, although its only requisite is implementing the IObservable<T>.Subscribe
method. This is the one called by client observers that wish to receive notifications.IObserver<T>
interface, but it's required that it implement three methods that will be called by the provider:IObserver<T>.OnNext
, which provides new or current informationIObserver<T>.OnError
, which is in charge of informing the observer about any error that has occurredIObserver<T>.OnCompleted
, which always marks the ending of notificationsSystem.Collections.Generics.List<T>
, which is in charge of holding references to the implementations of IObserver<T>
. This is a convenient way to handle references to an unlimited number of observers.In a real-case scenario, it might depend on the solution you're building: Windows Presentation Foundation interfaces implement observable collections precisely with this purpose. Even other mechanisms that implement the MVC paradigm are capable of showing this behavior.
A well-known case outside the OOP world is the AngularJS Framework, which makes every data in the model observable and linkable to the user interface, implementing a double binding architecture that makes any change in the model automatically reflect in the user interface, using a special markup (the moustache syntax).
The Strategy pattern is officially defined as a practice of defining a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.
Mainly, there are three participants:
Strategy
(or compositor) component responsible for defining a common interface for all algorithms to be managedConcreteStrategy
, which implements the algorithm using the strategy interfaceAContext
, which has three roles: it gets configured with the ConcreteStrategy
object, maintains a reference to the Strategy
object, and—optionally—it can define an interface to allow Strategy to access its data.In code, a typical example might be when you have to use different sorting strategies (in any collection), but depending on other circumstances, you might like to choose which sorting algorithm you'd like to use, for instance, QuickSort, Shell, or Bubble.
You can define an object (SortingClass
) with a method responsible for sorting, but depending on a value, the instance is created from another instance of the actual sorting method.
The following code gives you an idea about how to use this pattern. The key is that SortingClass
is called with distinct instances of the desired algorithm:
SortingStrategy shell = newSortingClass(newShell()); SortingStrategy quick = newSortingClass(newQuickSort()); SortingStrategy bubble = newSortingClass(newBubble());
With this approach, the user interface will always call the same method for sorting, whatever its name, but the actual sorting mechanism will be decided at runtime.
3.144.9.169