Els patrons de disseny són solucions provades i reutilitzables per problemes comuns en el desenvolupament de programari. Aquests patrons ajuden a crear codi més robust, mantenible i escalable. En aquesta secció, explorarem els conceptes bàsics dels patrons de disseny, els tipus de patrons més comuns i alguns exemples pràctics.

Conceptes Bàsics dels Patrons de Disseny

Què és un Patró de Disseny?

Un patró de disseny és una descripció o plantilla per resoldre un problema que es pot utilitzar en moltes situacions diferents. Els patrons de disseny no són codi específic, sinó més aviat una guia sobre com estructurar el codi per abordar un problema particular.

Beneficis dels Patrons de Disseny

  • Reutilització del Codi: Permeten reutilitzar solucions provades, reduint el temps de desenvolupament.
  • Mantenibilitat: Faciliten la comprensió i el manteniment del codi.
  • Escalabilitat: Ajuden a crear sistemes que poden créixer i adaptar-se a noves necessitats.
  • Comunicació: Proporcionen un llenguatge comú per als desenvolupadors, facilitant la comunicació d'idees complexes.

Tipus de Patrons de Disseny

Els patrons de disseny es poden classificar en tres categories principals:

  1. Patrons Creacionals: Ajuden a crear objectes de manera que s'adeqüin a la situació donada.
  2. Patrons Estructurals: Ajuden a compondre objectes i classes en estructures més grans.
  3. Patrons de Comportament: Ajuden a definir com els objectes interactuen i es comuniquen entre ells.

Patrons Creacionals

  • Singleton: Assegura que una classe només tingui una instància i proporciona un punt d'accés global a aquesta instància.
  • Factory Method: Defineix una interfície per crear un objecte, però permet a les subclasses alterar el tipus d'objecte que es crearà.
  • Abstract Factory: Proporciona una interfície per crear famílies d'objectes relacionats o dependents sense especificar les seves classes concretes.

Patrons Estructurals

  • Adapter: Permet que interfícies incompatibles treballin juntes.
  • Decorator: Afegeix funcionalitat addicional a un objecte de manera dinàmica.
  • Facade: Proporciona una interfície simplificada a un conjunt de classes o a un subsistema complex.

Patrons de Comportament

  • Observer: Defineix una dependència un-a-molts entre objectes de manera que quan un objecte canvia d'estat, tots els seus dependents són notificats i actualitzats automàticament.
  • Strategy: Permet definir una família d'algoritmes, encapsular-los i fer-los intercanviables.
  • Command: Encapsula una petició com un objecte, permetent així parametritzar clients amb cues, peticions i operacions reversibles.

Exemples Pràctics

Exemple 1: Singleton

El patró Singleton assegura que una classe només tingui una instància i proporciona un punt d'accés global a aquesta instància.

public class Singleton {
    private static Singleton instance;

    private Singleton() {
        // Constructor privat per evitar la creació de noves instàncies
    }

    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

Exemple 2: Observer

El patró Observer defineix una dependència un-a-molts entre objectes de manera que quan un objecte canvia d'estat, tots els seus dependents són notificats i actualitzats automàticament.

import java.util.ArrayList;
import java.util.List;

interface Observer {
    void update(String message);
}

class ConcreteObserver implements Observer {
    private String name;

    public ConcreteObserver(String name) {
        this.name = name;
    }

    @Override
    public void update(String message) {
        System.out.println(name + " received: " + message);
    }
}

class Subject {
    private List<Observer> observers = new ArrayList<>();

    public void addObserver(Observer observer) {
        observers.add(observer);
    }

    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }

    public void notifyObservers(String message) {
        for (Observer observer : observers) {
            observer.update(message);
        }
    }
}

Exemple 3: Strategy

El patró Strategy permet definir una família d'algoritmes, encapsular-los i fer-los intercanviables.

interface Strategy {
    void execute();
}

class ConcreteStrategyA implements Strategy {
    @Override
    public void execute() {
        System.out.println("Strategy A executed");
    }
}

class ConcreteStrategyB implements Strategy {
    @Override
    public void execute() {
        System.out.println("Strategy B executed");
    }
}

class Context {
    private Strategy strategy;

    public void setStrategy(Strategy strategy) {
        this.strategy = strategy;
    }

    public void executeStrategy() {
        strategy.execute();
    }
}

Exercicis Pràctics

Exercici 1: Implementar el Patró Singleton

Implementa una classe Singleton en el teu llenguatge de programació preferit. Assegura't que només es pugui crear una instància de la classe.

Exercici 2: Implementar el Patró Observer

Crea un sistema de notificació utilitzant el patró Observer. Implementa una classe Subject que pugui afegir, eliminar i notificar observadors.

Exercici 3: Implementar el Patró Strategy

Implementa una aplicació que utilitzi el patró Strategy per executar diferents algoritmes de càlcul (per exemple, sumar, restar, multiplicar).

Solucions

Solució 1: Singleton en Python

class Singleton:
    _instance = None

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super(Singleton, cls).__new__(cls)
        return cls._instance

Solució 2: Observer en Python

class Observer:
    def update(self, message):
        pass

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

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

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

    def add_observer(self, observer):
        self.observers.append(observer)

    def remove_observer(self, observer):
        self.observers.remove(observer)

    def notify_observers(self, message):
        for observer in self.observers:
            observer.update(message)

Solució 3: Strategy en Python

class Strategy:
    def execute(self):
        pass

class ConcreteStrategyA(Strategy):
    def execute(self):
        print("Strategy A executed")

class ConcreteStrategyB(Strategy):
    def execute(self):
        print("Strategy B executed")

class Context:
    def __init__(self):
        self.strategy = None

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

    def execute_strategy(self):
        self.strategy.execute()

Conclusió

Els patrons de disseny són eines poderoses per als desenvolupadors de programari. Ajuden a crear codi més net, mantenible i escalable. En aquesta secció, hem explorat els conceptes bàsics dels patrons de disseny, els tipus de patrons més comuns i alguns exemples pràctics. Amb la pràctica, els patrons de disseny es convertiran en una part natural del teu procés de desenvolupament.

Arquitectures de Sistemes: Principis i Pràctiques per Dissenyar Arquitectures Tecnològiques Robustes i Escalables

Mòdul 1: Introducció a les Arquitectures de Sistemes

Mòdul 2: Principis de Disseny d'Arquitectures

Mòdul 3: Components d'una Arquitectura de Sistemes

Mòdul 4: Escalabilitat i Rendiment

Mòdul 5: Seguretat en Arquitectures de Sistemes

Mòdul 6: Eines i Tecnologies

Mòdul 7: Casos d'Estudi i Exemples Pràctics

Mòdul 8: Tendències i Futur de les Arquitectures de Sistemes

© Copyright 2024. Tots els drets reservats