Since the creation of the first programming languages, programmers have noticed certain "patterns" emerge in the code that they were able to reuse to solve similar problems encountered later on. Eventually, these patterns were given formal names and definitions, giving developers a common vocabulary when describing and designing solutions to these problems.

Assumptions

What is a Design Pattern?

<div style="width:100%; margin:auto;text-align:center;"><img src="https://www.devmaking.com/img/topics/designpatterns/DesignPatternModeling_01.png" alt="UML diagram header" style="max-width:95%;"> </div>

A Design Pattern is template for solving a common problem in programming. Design Patterns are templates in the sense that there is no explicit way of coding the solution; allowing a pattern to remain an abstract idea that can be concretely implemented provides a programmer flexibility and control.

Types of Design Patterns

There are three main categories of design patterns; Creational, Structural, and Behavioral, which were made famous by the Gang of Four in their book "Design Patterns". There is an additional category of design patterns that deals with common problems in asynchronous systems: Concurrency Patterns, however that will not be touched on in this introduction.

A vast majority of known design patterns are geared towards Object-Oriented systems, as they deal with the creation, structure, and behavior of objects.

Creational Patterns

Creational design patterns are for, well.. creating things! When instantiating objects in an object oriented language, it's easy for the code to become complex, and complex code is often error prone. For this reason, creational patters offer a template for creating objects in a manageable and scalable way.

Common creational patterns include:

  • Abstract Factory
  • Builder
  • Singleton*
  • Object Pooling
  • Lazy initialization
  • Prototype

> Whether or not the Singleton is considered an "anti-pattern" is often debated among developers, as some encourage them while others are strongly against their use.

Structural Patterns

Structural patterns offer common ways that relationships between objects can be realized.

For example, if you need to model the relationship between a manager and the team they manage, you might use a simple array of objects to represent them. However, what if a manager had a manager as well, being that they too are employees? The composite pattern specifically addresses tree-like relationships which are especially prevalent in company organizational structure.

More structural patterns include:

  • Adapter
  • Bridge
  • Decorator
  • Facade
  • Flyweight
  • Proxy

Behavioral Patterns

Structural patterns describe common relationships between objects while behavioral patterns describe the common ways that objects communicate.

When a video game GUI wants to display the current score, it might observe the object that manages the score, and when the player scores more points, the data object will notify the GUI, allowing the score to be updated without the score manager needing to get it's hand dirty with managing both the data and updating the GUI. The pattern we just described is called the Observer pattern, sometimes also referenced as the Subscriber pattern.

More behavioral patterns include:

  • Command
  • Chain of Responsibility
  • Iterator
  • State
  • Strategy
  • Template
  • Visitor
  • Null Object

Anti-Patterns

A Pattern that has a name and is widely used does not necessarily mean that it is a good thing; some patterns are often bad practice and rightly dubbed anti-patterns.

Anti-Patterns are common templates which, while well-meant, often create more problems than they solve. This definition is especially true when alternative solutions exist that are proven to be much more effective.

Some Anti-patterns include:

  • Big Ball of Mud (Spaghetti Code): When a project shows no sign of structure in the code.
  • Gold-Plating: Continuing work on a project where extra work adds little to no value.
  • Interface Bloat: An interface with so many method definitions that it is hard to implement.
  • God Object: When one class contains too many functions.
  • Poltergeists: If an object only exists to send information to another object.
  • Lasagna Code: Having too many layers of abstraction in a design, making things difficult to change or understand.
  • Magic Numbers: Unexplained constants in code, where it could be better explained with a variable.

The Singleton Debate

To pick up on a point touched on earlier, there are some developers who consider the Singleton to be an anti-pattern. In short, a singleton works by maintaining a unique instance of itself; only one instance of a singleton object can exist, allowing it to be accessed from anywhere in a program. This is often used for maintaining global state of an application or particular piece of data.

The main argument for the Singleton being an anti-pattern is that it tends to be misused and becomes a taboo similar to using "global" variables in an application, which ultimately leads to more problems than problems solved. However, there are still uses where singletons offer a practical solution to the problem without inducing unnecessary complexity, making it a strongly opinionated pattern.

Design Patterns as Language Features

For an intermediate developer, subroutines might seem like a standard language feature; a language without them built-in wouldn't be nearly as powerful. However, there was a time when this wasn't the case, and instead subroutines existed as design patterns! This has more recently been observed with iterators in modern languages such as C#, Python, and Java where you can use a for (item in collection) syntax to iterate over a list.

This has led to debate as to whether design patterns are evidence of shortcomings in a particular language, and are sometimes used as criticisms of the OOP paradigm in general. Luckily for programmers, most languages do not explicitly just one paradigm.

Being Pragmatic with Design Patterns

Perhaps the biggest mistake that a novice programmer makes when learning about design patterns is that they start looking for places to use them in code.

In an educational setting, practicing design patterns is useful to expose the programmer to new concepts, and helps them recognize when design patterns occur naturally. Outside of the educational setting, though, design patterns should emerge from the code, and not be shoehorned into the design to be hip and trendy.

Newer programmers might think they need to implement design patterns because they sound cool and professional, and they would often be misled by this thought. Ultimately, a design pattern is nothing more than a tool, and it takes practice to know when it's the right tool!

<div style="width:100%; margin:auto;text-align:center;"><img src="https://www.devmaking.com/img/topics/designpatterns/IntroToDesignPatterns_01.png" alt="Utility belt!" style="width:600px;max-width:95%;"> </div>

You might find yourself in a situation where you're able to utilize a design pattern, but ultimately you should only use it if it makes your life easier!

In practice, some programmers find it useful to write code as naturally as possible first, then review later for places that it would make more sense to use a design pattern than the current design. Sure the code might work, but caring about your code enough to continuously make it better is a mark of a professional programmer.

Lastly, you won't always encounter situations where a design pattern shows itself explicitly; sometimes a class might blend between a few patterns, like a flyweight that can be called like a singleton. Being a great developer requires looking beyond the basics for designing a clean, robust solution, and the best way to become a great developer is to code more!

Further Resources