Working with design patterns during software development can be tricky at times. Their purpose is to provide guidelines for dealing with particular problems that might arise during development. However, the sheer quantity of potential design patterns, combined with the intricacies of each pattern's inner workings, can often make it challenging to select the correct pattern for your particular project, component, or programming language.
This article will attempt to provide a brief overview of what design patterns are in the realm of software development, and serve as a jumping-off point for future articles, which will further detail each design pattern, including code examples using C#
.
The groundbreaking book Design Patterns: Elements of Reusable Object-Oriented Software, published in 1995, has sold hundreds of thousands of copies to date, and is largely considered one of the foremost authorities on object-oriented theory and software development practices. In fact, the contents of the book was so influential that the four authors have since been given the nickname: The Gang of Four (GoF
).
The book is roughly split into two parts. Whereas Chapter 1 deals with general object-oriented design techniques, Chapter 2 introduces a total of 23
different software design patterns, split into three basic categories: Creational
, Structural
, and Behavioral
. Spanning across seven different example design problems, the various design patterns are shown to be applicable across these seven scenarios, resulting in one of the first published examples of modern software design patterns.
Some criticism was directed at the book shortly after publication. Much of this criticism focused on the belief that many of the proposed design patterns, which were originally written using the C++
language, were merely workarounds to deal with missing features of the originating language they were written in. Counterarguments were made that other languages at the time (Lisp, Dylan, AspectJ, etc), were able to eliminate the need for the majority of the 23
patterns, on account of the syntax and components presented by these other languages.
In the decades since the initial publication of Design Patterns, most modern languages have adopted techniques and syntax for built-in support for many of these design patterns, while others remain largely unnecessary. To provide the most applicable and real-world information possible, we'll briefly define each of the original patterns in the sections below. Then, in future articles that explore the patterns in more detail, using C#
code examples, we'll focus on the most relevant patterns that are likely to be used in modern development.
Creational patterns
emphasize the automatic creation of objects within code, rather than requiring you to instantiate objects directly. In most cases, this means that a function or method can call the code necessary to instantiate new objects on your behalf, so you only need to explicitly modify that object creation when it is necessary, and allow default behaviors to take over otherwise.
polymorphism
, the concept in object-oriented programming that allows one interface
to serve as a basis for multiple functions of different types
.construction
of an object from its representation
. This is usually done by defining a Builder
object that presents methods to update the object, without directly interacting with the object itself.type
of object to be created. As the name suggests, this object instantiation is performed through a secondary Factory
class, again using polymorphism
.prototyping
or cloning a prototypical instance
of an object. Effectively, an abstract Prototype
class is created, and from that base prototype, new secondary inherited
classes are defined.Structural patterns
focus on the composition of classes and objects. By using inheritance
and interfaces
, these patterns allow objects to be composed in a manner that provides new functionality. In most cases, an interface
in object-oriented programming is an abstract type or class which has no logical code, but instead is used to define method signatures and behaviors for other classes that will implement the interface
.
interface
, which is otherwise incompatible, to be adapted to fit a new class. Typically, this is performed by creating a new ClassNameAdapter
class that implements the interface
, allowing for compatibility across the system.composition
type, their behavior should be identical when combined into a collective group.decorator
class. This pattern is commonly used when an object is instantiated, but as code execution progresses, modifications must be made to the object before it is finalized.facade
) object that obfuscates and simplifies interactions between it and the more complicated interface behind it. Commonly used when a complex series of actions must take place to perform a task, where executing each and every task, in order, is too complicated. Instead, a simple facade
replaces that series of tasks with a single task to be executed.Factory
-style patterns to access and store already generated data during future executions.proxy
class attaches additional behavior onto the wrapped object, without the need to modify the base object class behavior.Behavioral patterns
are concerned with communication and assignment between objects.
chain of command
during execution, such that the first object is used, then the second, and so on. Often used as a failsafe in applications, checking the validity of the primary object, before moving onto the secondary object if the primary fails, and so forth.interface
, an object can specify individual behavior when a particular command is invoked, while a different object type can use that same command, but invoke its own unique behavior instead.interpret
language syntax from a provided sentence. Typically, each symbol is defined by one class, and then a syntax tree is used to parse (interpret
) the overall sentence.mediator
) that acts as a go-between for interactions between two other similar objects (colleagues
). Commonly, this is used when multiple objects need to communicate, but do not (or should not) be aware of the others respective implementation or behavior.undo
feature.observed
object causes the observer
objects to be notified. Typically, this is found in many languages that utilize asynchronous
functionality, which requires events to be observed
and responded to outside of typical execution order.state
. While these states
are often changed throughout execution, the implementation of each possible state
is typically defined by a unique class interface
.strategy
changes based on the current situation. This is merely an object-oriented extension of common if-else
statements, by altering the execution of code based on the outcome of previous code.template
to be used as the basis for execution, without defining the inner-workings of any individual class or object. This is commonly seen in web applications, where the visual interface of the application is generated using templates
, which are created using underlying data, but neither the template
nor the underlying data are aware of the implementation of the other.visitor
class defines unique methods that are shared between it and other objects, without the need for the other object to be aware of the additional functionality.While this is just a brief description of each design pattern, we hope this serves as a good basis for understanding just how varied design patterns can be, and how useful as well. In future articles, we'll dive deeper into specific design patterns, and examine how they might be implemented in real-world scenarios, using actual code examples.