Design Patterns: Intro

Reusable Tools

Software design patterns are powerful components that can be reused, and incrementally refactored to suit particular purposes. As with the cookie cutter above, just because it will always produce the shape of cookie doesn’t mean you can’t use different decorations.

This is an introductory post to the concept of software design patterns and their general uses as part of an ongoing series to breakdown and explain each one.

Software design patterns are not like software design principles or software design methodologies. Both of those concepts are purely abstract, and deal more with the philosophical approach to writing software. Patterns, while still mostly abstract, have a considerable concrete component. While most patterns can be implemented in a variety of specific methods, those implementations can be modified in small ways that can completely change the nature of the code.

For example, let’s take a look at one of the most basic patterns, the Factory Pattern. The Factory Pattern is a Creator Pattern that allows calling code to create an instance of some object without knowing the details of its construction:

public interface IFactory
{
    object CreateInstance();
}

This factory creates an instance of some object. That’s all very abstract, so let’s make it a little more tangible. We’re talking about “factories”, so let’s make a factory that creates something we know comes from factories:

public interface ICarFactory
{
    Car CreateCar();
}

The details of this factory can be implemented for a variety of different car manufacturers:

public class FordCarFactory : ICarFactory
{
    public Car CreateCar() { return new Mustang(); }
}

public class ToyotaCarFactory : ICarFactory
{
    public Car CreateCar() { return new Camry(); }
}

public class NissanCarFactory : ICarFactory
{
    public Car CreateCar() { return new Altima(); }
}

This is all pretty straightforward. But think about what goes into those cars. Each car has its own set of components, and those components, in turn, must be created somewhere:

public interface IWheelFactory
{
    Wheel CreateWheel();
}

public interface IChasisFactory
{
    Chasis CreateChasis();
}

public interface IWindowFactory
{
    Window CreateWindow();
}

And so on. This is where the concept of the pattern starts to come into focus. By refactoring the original IFactory we were able to create an ICarFactory that returns Car instances instead of unspecified objects. By refactoring that, we can simply exchange the “Car” keyword with any car component to create a factory for those components.

This is how software design patterns work. They codify solutions to general types of problems into a straightforward, reusable model that can be subtly (and not-so-subtly) modified to suit a variety of specific situational needs.

As such, there are several different categories of design patterns, each with its own list of included patterns:

Creator Patterns

  • Factory Pattern
  • Abstract Factory Pattern
  • Builder Pattern
  • Singleton Pattern
  • Multition Pattern
  • Object Pool Pattern
  • Prototype Pattern
  • Lazy Initialization Pattern
  • Dependency Injection Pattern

Structural Patterns

  • Adapter Pattern
  • Decorator Pattern
  • Facade Pattern
  • Bridge Pattern
  • Composite Pattern
  • Twin Pattern
  • Marker Pattern
  • Module Pattern

Behavioral Patterns

  • Command Pattern
  • Interpreter Pattern
  • Iterator Pattern
  • Chain of Responsibility Pattern
  • Mediator Pattern
  • Null Object Pattern
  • Servant Pattern
  • Specification Pattern
  • State Pattern
  • Strategy Pattern
  • Vistor Pattern

Concurrency Patterns

  • Lock Pattern
  • Read-Write Locking Pattern
  • Double-Checked Locking Pattern
  • Monitor Object Pattern
  • Thread Pool Pattern
  • Balking Pattern
  • Scheduler Pattern
  • Reactor Pattern

Conclusion

Each of these types of design pattern is meant to solve a different type of problem. Chances are, in any software engineer’s career they will have to solve many of the problems over and over again. Knowing the appropriate pattern to use in a specification situation will greatly reduce the amount of time it takes to implement a suitable, if not optimal solution.