What are Design Patterns?

   



In the programming world, design patterns are recurring solutions to common problems in software development. They are documented best practices that help programmers write more organized, reusable, and maintainable code.

🔗 Do you like Techelopment? Check out the site for all the details!

Origins and the Gang of Four

The concept of design patterns was popularized by the book Design Patterns: Elements of Reusable Object-Oriented Software, published in 1994 by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides. These four authors are known as the Gang of Four (GoF). In their work, they cataloged 23 fundamental design patterns, dividing them into three main categories:

  1. Creational – They focus on managing object creation, improving the flexibility and reusability of the code.

    • Singleton: Ensures that a class has only one instance and provides a global access point to that instance.

    • Factory Method: Defines an interface to create an object, but lets subclasses decide which class to instantiate. Helps decouple object creation from its implementation.

    • Abstract Factory: Provides an interface to create families of related objects without specifying their concrete classes. Useful for creating mutually compatible products.

    • Builder: It separates the complex construction of an object from its representation, allowing the creation of different objects with the same construction process.

    • Prototype: It allows you to create new objects by cloning an existing object, reducing the need to create new objects from scratch and improving performance in some cases.

  2. Structural – They concern the composition of classes and objects, facilitating the design of more efficient structures.

    • Adapter: Converts a class's interface to another interface that the client expects. Allows classes with incompatible interfaces to work together.

    • Bridge: It decouples an interface from its implementation, allowing both to vary independently.

    • Composite: Allows you to treat individual objects and compositions of objects uniformly. Used for tree structures, such as file hierarchies or graphical components.

    • Decorator: Dynamically adds functionality to an object, extending its behavior without changing its structure.

    • Facade: Provides a simplified interface to a complex set of classes or libraries. Helps reduce complexity by hiding implementation details.

    • Flyweight: It shares expensive-to-create objects, using minimal memory to improve performance when there are many similar objects.

    • Proxy: Controls access to another object, acting as a proxy or "middleman". Can implement access control, protection, caching, and more.

  3. Behavioral – They deal with the interaction and communication between objects.

    • Chain of Responsibility: It allows a request to be passed along a chain of handlers, with each handler deciding whether to handle or pass the request on to the next.

    • Command: It encapsulates a request as an object, allowing you to parameterize clients with different requests, delay execution, and track requests.

    • Interpreter: Provides a way to represent and interpret a language. It is useful for building interpreters of languages ​​or expressions.

    • Iterator: Provides a way to access the elements of a collection without exposing the underlying structure.

    • Mediator: Defines an object that encapsulates how a set of objects interact, reducing the need for direct communication between objects, improving decoupling.

    • Memento: Allows you to save and restore the internal state of an object without breaking encapsulation, useful for implementing undo/redo functionality.

    • Observer: It allows an object (subject) to notify other objects (observers) when its state changes, without the subject needing to know the details of the observers.

    • State: Allows an object to behave differently based on its internal state, dynamically changing the object's behavior.

    • Strategy: It defines a family of algorithms, encapsulating them and making them interchangeable. It allows you to change the algorithm used by an object without changing its structure.

    • Template Method: It defines the structure of an algorithm, leaving some steps to subclasses that can implement or override the details.

    • Visitor: It allows you to add new operations to an object tree without modifying the object classes themselves, by visiting each element of the tree and applying the desired operation.

Why use Design Patterns?

Using design patterns brings several benefits, including:

  • Reusability: helps avoid rewriting code for problems that have already been solved.

  • Maintainability: makes the code more understandable and easier to modify.

  • Scalability: makes it easier to adapt your code to new features.

  • Communication: makes it easier for developers to understand code, thanks to a common language based on consolidated patterns.

Design patterns are essential tools for software developers, allowing them to address common problems with proven and efficient solutions. Studying and applying the Gang of Four patterns helps to write more robust, readable and scalable code while reducing complexity.

Let's see some examples of Design Patterns in detail

1. Singleton (Creational)

Singleton ensures that a class has only one instance and provides a global access point to it.

JavaScript example:

class Singleton {
    constructor() {
        if (!Singleton.instance) {
            Singleton.instance = this;
        }
        return Singleton.instance;
    }
}

const instance1 = new Singleton();
const instance2 = new Singleton();
console.log(instance1 === instance2); // Output: true

2. Decorator (Structural)

The Decorator allows you to dynamically add functionality to an object without modifying its original structure.

JavaScript example:

class Coffee {
    cost() {
        return 5;
    }
}

class MilkDecorator {
    constructor(coffee) {
        this.coffee = coffee;
    }
    cost() {
        return this.coffee.cost() + 2;
    }
}

const simpleCoffee = new Coffee();
const coffeeWithMilk = new MilkDecorator(simpleCoffee);
console.log(coffeeWithMilk.cost()); // Output: 7

3. Observer (Behavioral)

The Observer defines a one-to-many dependency between objects so that when an object changes state, all of its observers are automatically notified.

JavaScript example:

class Subject {
    constructor() {
        this.observers = [];
    }
    addObserver(observer) {
        this.observers.push(observer);
    }
    notify(data) {
        this.observers.forEach(observer => observer.update(data));
    }
}

class Observer {
    update(data) {
        console.log(`Notified with data: ${data}`);
    }
}

const subject = new Subject();
const observer1 = new Observer();
const observer2 = new Observer();

subject.addObserver(observer1);
subject.addObserver(observer2);
subject.notify("New update");



Follow me #techelopment

Official site: www.techelopment.it
facebook: Techelopment
instagram: @techelopment
X: techelopment
Bluesky: @techelopment
telegram: @techelopment_channel
whatsapp: Techelopment
youtube: @techelopment