DEV Community

Cover image for 7+ Design Patterns Every Developer Must Know
Marco Pineda
Marco Pineda

Posted on

7+ Design Patterns Every Developer Must Know

8 Design Patterns Every Developer Must Know

When one begins a career in software development, design patterns are like a treasure map guiding you toward efficient and scalable solutions. They’re tried-and-true approaches to solving common problems, helping you write cleaner and more maintainable code. Let’s explore eight essential design patterns every developer should know, grouped into three categories: Creational, Behavioral, and Structural patterns. Grab your coffee, and let’s get started!


1. Creational Patterns

Creational patterns focus on how objects are created. Instead of instantiating objects directly, these patterns provide solutions to make the process more flexible and reusable.

1.1 Factory Pattern

Imagine you’re running a bakery. Depending on the order, you might create a cake, a muffin, or a loaf of bread. The Factory pattern works the same way: it provides a single interface to create objects, but the actual object created depends on the input.

Creational Example:

class Notification:
    def notify(self):
        pass

class EmailNotification(Notification):
    def notify(self):
        print("Sending Email Notification")

class SMSNotification(Notification):
    def notify(self):
        print("Sending SMS Notification")

class NotificationFactory:
    @staticmethod
    def get_notification(method):
        if method == "email":
            return EmailNotification()
        elif method == "sms":
            return SMSNotification()
        else:
            raise ValueError("Unknown notification method")

# Usage
notification = NotificationFactory.get_notification("email")
notification.notify()

Enter fullscreen mode Exit fullscreen mode

1.2 Builder Pattern

Ever ordered a custom burger? You specify the bun, patty, toppings, and sauces, and voila! The Builder pattern lets you construct complex objects step by step, giving you complete control over the creation process.

Builder Example:

class Burger:
    def __init__(self):
        self.ingredients = []

    def add_ingredient(self, ingredient):
        self.ingredients.append(ingredient)

    def show(self):
        print(f"Burger with: {', '.join(self.ingredients)}")

class BurgerBuilder:
    def __init__(self):
        self.burger = Burger()

    def add_bun(self):
        self.burger.add_ingredient("Bun")
        return self

    def add_patty(self):
        self.burger.add_ingredient("Patty")
        return self

    def add_lettuce(self):
        self.burger.add_ingredient("Lettuce")
        return self

    def build(self):
        return self.burger

# Usage
builder = BurgerBuilder()
burger = builder.add_bun().add_patty().add_lettuce().build()
burger.show()

Enter fullscreen mode Exit fullscreen mode

1.3 Singleton Pattern

Think of a Singleton as the VIP pass to a club. Only one person—or instance—can hold the pass at a time. The Singleton pattern ensures that a class has only one instance and provides a global access point to it.

Singleton Example:

class Singleton:
    _instance = None

    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            cls._instance = super().__new__(cls, *args, **kwargs)
        return cls._instance

# Usage
singleton1 = Singleton()
singleton2 = Singleton()
print(singleton1 is singleton2)  # Output: True

Enter fullscreen mode Exit fullscreen mode

2. Behavioral Patterns

Behavioral patterns focus on how objects interact and communicate. They help define the flow and logic of complex systems.

2.1 Observer / PubSub Pattern

Picture a social media app. You follow your favorite influencer, and every time they post, you get notified. The Observer pattern works similarly: objects (observers) subscribe to another object (subject) and get updates when the subject changes.

Observer/PubSub Example:

class Subject:
    def __init__(self):
        self._observers = []

    def subscribe(self, observer):
        self._observers.append(observer)

    def unsubscribe(self, observer):
        self._observers.remove(observer)

    def notify(self):
        for observer in self._observers:
            observer.update()

class Observer:
    def update(self):
        pass

class ConcreteObserver(Observer):
    def __init__(self, name):
        self.name = name

    def update(self):
        print(f"{self.name} received notification")

# Usage
subject = Subject()
observer1 = ConcreteObserver("Observer 1")
observer2 = ConcreteObserver("Observer 2")

subject.subscribe(observer1)
subject.subscribe(observer2)
subject.notify()

Enter fullscreen mode Exit fullscreen mode

2.2 Iterator Pattern

Imagine flipping through a photo album. The Iterator pattern gives you a way to access elements of a collection (like photos) one by one without exposing the underlying structure.

Iterator Example:

class Iterator:
    def __init__(self, collection):
        self._collection = collection
        self._index = 0

    def has_next(self):
        return self._index < len(self._collection)

    def next(self):
        if not self.has_next():
            raise StopIteration
        item = self._collection[self._index]
        self._index += 1
        return item

# Usage
collection = [1, 2, 3, 4]
iterator = Iterator(collection)
while iterator.has_next():
    print(iterator.next())

Enter fullscreen mode Exit fullscreen mode

2.3 Strategy Pattern

Think about choosing a route on a GPS app. Depending on your preference (fastest, shortest, or scenic), the app changes its behavior. The Strategy pattern allows you to switch algorithms dynamically at runtime.

Strategy Example:

class Strategy:
    def execute(self, data):
        pass

class FastestRoute(Strategy):
    def execute(self, data):
        print("Calculating the fastest route")

class ScenicRoute(Strategy):
    def execute(self, data):
        print("Calculating the scenic route")

class Navigator:
    def __init__(self, strategy):
        self.strategy = strategy

    def set_strategy(self, strategy):
        self.strategy = strategy

    def navigate(self, data):
        self.strategy.execute(data)

# Usage
navigator = Navigator(FastestRoute())
navigator.navigate("Destination")
navigator.set_strategy(ScenicRoute())
navigator.navigate("Destination")

Enter fullscreen mode Exit fullscreen mode

3. Structural Patterns

Structural patterns are all about how objects are organized and how they work together to form larger structures.

3.1 Facade Pattern

Imagine walking into a hotel. Instead of speaking to the chef, cleaner, and receptionist separately, you deal with one person at the front desk. The Facade pattern provides a simplified interface to a complex subsystem.

Facade Example:

class Hotel:
    def book_room(self):
        print("Room booked")

class Flight:
    def book_flight(self):
        print("Flight booked")

class TravelFacade:
    def __init__(self):
        self.hotel = Hotel()
        self.flight = Flight()

    def book_trip(self):
        self.hotel.book_room()
        self.flight.book_flight()

# Usage
facade = TravelFacade()
facade.book_trip()

Enter fullscreen mode Exit fullscreen mode

3.2 Adapter Pattern

Ever used a travel adapter to plug your devices into a foreign socket? The Adapter pattern bridges the gap between two incompatible interfaces, allowing them to work together seamlessly.

Adapter Example:

class OldSystem:
    def old_method(self):
        print("Old system method")

class NewSystem:
    def new_method(self):
        print("New system method")

class Adapter:
    def __init__(self, old_system):
        self.old_system = old_system

    def new_method(self):
        self.old_system.old_method()

# Usage
old_system = OldSystem()
adapter = Adapter(old_system)
adapter.new_method()

Enter fullscreen mode Exit fullscreen mode

Wrapping It Up

Design patterns are the secret weapon in a developer’s toolkit. They save time, reduce bugs, and improve your code’s readability. Whether you’re creating objects more flexibly, managing communication between components, or simplifying complex systems, these eight patterns will have your back.

Which pattern is your favorite? Or is there one you’re excited to try? Let’s chat in the comments below—I’d love to hear your thoughts!

Top comments (0)