8 Design Patterns Every Developer Should Know: From Factory to Facade

8 Design Patterns Every Developer Should Know: From Factory to Facade

4
Futuristic digital abstract art representing software architecture, blueprints of object-oriented code structures, glowing geometric shapes connecting in a network, dark background with neon blue and purple accents

In 1994, the "Gang of Four" released Design Patterns, a seminal book that introduced 23 object-oriented design patterns. These patterns generally fall into three buckets: creational, structural, and behavioral.

While the book is 30 years old, the concepts remain relevant. In an industry where JavaScript frameworks go out of style rapidly, foundational architecture tends to stick around.

Here are eight design patterns that help solve common software problems.

1. The Factory Pattern

Category: Creational

The Factory pattern is about delegation. Imagine you want a burger. You do not want to source the ingredients, grind the meat, and bake the bun yourself. You just want to order the burger and have it handed to you.

We apply this same logic to code. If creating an object requires a complex list of ingredients (dependencies), we use a Factory to instantiate the object and return it to us. Whether you need a standard cheeseburger, a deluxe version, or a vegan option, you simply tell the Factory what type you want.

Conceptual illustration of the Factory Design Pattern using a burger restaurant analogy. A central machine (Factory) outputting a complete burger box based on a simple order ticket, hiding the ingredient assembly process

The Factory handles the instantiation logic, so the client code doesn't have to. However, this means you don't always know exactly how the object is constructed inside the "black box."

2. The Builder Pattern

Category: Creational

If you need more control over how an object is constructed, the Builder pattern is a better choice. This is useful when you don't want to pass in every single parameter at once or when an object has many optional configurations.

Using the burger analogy, imagine a custom order kiosk. You have an individual method for adding each ingredient.

  • addBun()
  • addPatty()
  • addCheese()
Visual representation of the Builder Pattern showing a burger being assembled layer by layer. Step 1: Bun, Step 2: Patty, Step 3: Cheese. Arrows indicating method chaining returning the builder reference

Each method adds a specific part and returns a reference to the Builder itself, allowing you to chain methods together. Finally, a build() method returns the finished product. This is frequently used in systems like Protocol Buffers at Google to construct complex messages cleanly.

3. The Singleton Pattern

Category: Creational

A Singleton is a class that allows only a single instance of itself to exist. This is particularly useful for maintaining a single copy of an application's state, such as tracking if a user is logged in.

Diagram of the Singleton Pattern. A single glowing golden object (Application State) in the center, with multiple distinct threads or components pointing to it, symbolizing a shared single source of truth

To implement this, you generally use a static instance variable and a static method often called getAppState. This method acts as a gatekeeper:

  1. It checks if an instance already exists.
  2. If it doesn't, it instantiates one.
  3. If it does, it returns the existing instance.

This ensures that multiple components in your app refer to the exact same source of truth. If one component updates the state, the change is reflected everywhere.

4. The Observer Pattern (Pub/Sub)

Category: Behavioral

Illustration of the Observer Pattern (Pub/Sub) depicted as a central YouTube logo broadcasting a signal wave to multiple diverse devices (phones, laptops) representing subscribers receiving a notification simultaneously

5. The Iterator Pattern

Category: Behavioral

The Iterator pattern defines how to traverse the values in an object. Simple arrays often have built-in iterators (like using the in keyword in Python), but complex data structures like binary search trees or linked lists require custom logic.

Technical diagram of the Iterator Pattern applied to a Linked List. Visual nodes connected by arrows, with a magnifying glass or pointer highlighting the 'Current' node moving sequentially to the 'Next' node

For a linked list, you might define an iterator with a pointer to the current node.

  • Init: Sets the current pointer to the head of the list.
  • Next: Returns the current value and shifts the pointer to the next node.
  • Stop: Sends a signal when the end of the list is reached.

This provides a uniform interface for traversing different types of collections without the user needing to manage pointers manually.

6. The Strategy Pattern

Category: Behavioral

The Strategy pattern allows you to modify or extend the behavior of a class without changing its code directly. This helps adhere to the Open/Closed principle.

Flowchart representing the Strategy Pattern. A central data processor box with a slot where different strategy cartridges (labeled 'Remove Negatives' and 'Remove Odds') can be swapped in to change the output flow

Suppose you have a method that filters an array. You might want to filter out negative values, or perhaps filter out odd values. Instead of writing multiple methods or complex if/else logic inside the class, you define a "Strategy" interface.

You create separate implementations for each filtering logic (strategies). At runtime, you pass the specific strategy you want into the filter method. This allows you to add new filtering strategies in the future without touching the original class code.

7. The Adapter Pattern

Category: Structural

This pattern works exactly like real-world hardware adapters. If you have a screw that is too small for a hole, or a Micro-USB cable that needs to fit into a standard USB port, you need an adapter.

Close-up 3D render of a Micro-USB cable plugging into a custom adapter block, which then plugs into a standard USB port, symbolizing the Adapter Pattern making incompatible interfaces work together

In code, this bridges the gap between incompatible interfaces. If you have a legacy class that doesn't fit the interface your system expects, you create an Adapter class. This adapter extends the target interface but is composed of the incompatible class (the "Micro-USB cable"). It translates calls from the standard interface to the specific methods of the wrapped class.

8. The Facade Pattern

Category: Structural

A facade is an outward appearance used to conceal a less pleasant reality. in programming, the "less pleasant reality" is complex, low-level code that we want to hide.

Illustration of the Facade Pattern. A clean, simple control panel with a single button labeled 'Start' covering a background of complex, messy gears, wires, and machinery, representing the abstraction of complexity

A Facade is a wrapper class that abstracts away these details. Common examples include:

  • HTTP Clients: You make a simple get request, while the client handles the TCP/IP handshake, headers, and packet reassembly behind the scenes.
  • Dynamic Arrays: Vectors in C++ or ArrayLists in Java automatically resize memory allocation under the hood so you don't have to manage it manually.

The goal is to provide a clean, simple interface for programmers to interact with, keeping the complexity hidden.