Els patrons estructurals són un tipus de patrons de disseny que es centren en com compondre classes i objectes per formar estructures més grans i complexes. Aquests patrons ajuden a garantir que les relacions entre les entitats del sistema siguin flexibles i eficients, permetent una millor gestió del codi i una major facilitat de manteniment.

Objectius dels Patrons Estructurals

  1. Simplificar les relacions entre objectes: Faciliten la composició d'objectes complexos a partir de components més simples.
  2. Reduir la complexitat del codi: Mitjançant l'ús de patrons estructurals, es pot reduir la complexitat del codi, fent-lo més fàcil de llegir i mantenir.
  3. Millorar la reutilització del codi: Permeten reutilitzar components de codi en diferents parts del sistema o en diferents projectes.
  4. Facilitar l'escalabilitat: Ajuden a crear sistemes que poden créixer i evolucionar de manera més fàcil i controlada.

Tipus de Patrons Estructurals

Els patrons estructurals es poden dividir en diverses categories, cadascuna amb el seu propi propòsit i aplicació. A continuació es presenten els principals patrons estructurals que es tractaran en aquest mòdul:

  1. Adapter: Permet que classes amb interfícies incompatibles treballin juntes.
  2. Bridge: Separa l'abstracció de la seva implementació perquè ambdues puguin variar independentment.
  3. Composite: Composa objectes en estructures d'arbre per representar jerarquies part-tot.
  4. Decorator: Afegeix funcionalitat addicional a un objecte de manera dinàmica.
  5. Facade: Proporciona una interfície simplificada a un conjunt de classes o a un subsistema complex.
  6. Flyweight: Utilitza la compartició per suportar grans quantitats d'objectes petits de manera eficient.
  7. Proxy: Proporciona un substitut o marcador de lloc per a un altre objecte per controlar l'accés a aquest.

Beneficis dels Patrons Estructurals

  • Flexibilitat: Els patrons estructurals permeten canviar l'estructura del sistema sense afectar el seu comportament.
  • Mantenibilitat: Faciliten la comprensió i manteniment del codi, ja que les relacions entre objectes estan ben definides.
  • Reutilització: Promouen la reutilització de codi, ja que els components poden ser fàcilment combinats i reutilitzats en diferents contextos.
  • Escalabilitat: Ajuden a crear sistemes que poden escalar de manera eficient, afegint nous components sense necessitat de canviar els existents.

Exemple Pràctic: Patró Adapter

Per il·lustrar com funciona un patró estructural, considerem el patró Adapter. Aquest patró permet que dues interfícies incompatibles treballin juntes. A continuació es presenta un exemple en codi Java:

// Interfície objectiu
interface Target {
    void request();
}

// Classe existent amb una interfície incompatible
class Adaptee {
    void specificRequest() {
        System.out.println("Request específica");
    }
}

// Classe Adapter que fa compatible Adaptee amb Target
class Adapter implements Target {
    private Adaptee adaptee;

    public Adapter(Adaptee adaptee) {
        this.adaptee = adaptee;
    }

    @Override
    public void request() {
        adaptee.specificRequest();
    }
}

// Client que utilitza la interfície Target
public class Client {
    public static void main(String[] args) {
        Adaptee adaptee = new Adaptee();
        Target target = new Adapter(adaptee);
        target.request();  // Sortida: Request específica
    }
}

Explicació del Codi

  1. Interfície Target: Defineix la interfície que el client espera utilitzar.
  2. Classe Adaptee: Conté la funcionalitat que volem adaptar, però la seva interfície és incompatible amb la que el client espera.
  3. Classe Adapter: Implementa la interfície Target i conté una instància de Adaptee. En el mètode request, crida al mètode specificRequest de Adaptee.
  4. Client: Utilitza la interfície Target per interactuar amb Adaptee a través de l'Adapter.

Exercici Pràctic

Exercici 1: Implementar el Patró Adapter

Implementa el patró Adapter per adaptar una classe OldSystem amb un mètode oldMethod a una interfície NewSystem amb un mètode newMethod.

Solució

// Interfície NewSystem
interface NewSystem {
    void newMethod();
}

// Classe OldSystem amb una interfície incompatible
class OldSystem {
    void oldMethod() {
        System.out.println("Mètode antic");
    }
}

// Classe Adapter que fa compatible OldSystem amb NewSystem
class SystemAdapter implements NewSystem {
    private OldSystem oldSystem;

    public SystemAdapter(OldSystem oldSystem) {
        this.oldSystem = oldSystem;
    }

    @Override
    public void newMethod() {
        oldSystem.oldMethod();
    }
}

// Client que utilitza la interfície NewSystem
public class AdapterExample {
    public static void main(String[] args) {
        OldSystem oldSystem = new OldSystem();
        NewSystem newSystem = new SystemAdapter(oldSystem);
        newSystem.newMethod();  // Sortida: Mètode antic
    }
}

Explicació de la Solució

  1. Interfície NewSystem: Defineix la nova interfície que el client espera utilitzar.
  2. Classe OldSystem: Conté la funcionalitat antiga que volem adaptar.
  3. Classe SystemAdapter: Implementa la interfície NewSystem i conté una instància de OldSystem. En el mètode newMethod, crida al mètode oldMethod de OldSystem.
  4. Client: Utilitza la interfície NewSystem per interactuar amb OldSystem a través de SystemAdapter.

Conclusió

Els patrons estructurals són essencials per a la creació de sistemes de programari flexibles i mantenibles. A través de l'ús d'aquests patrons, podem simplificar les relacions entre objectes, millorar la reutilització del codi i facilitar l'escalabilitat dels nostres sistemes. En els següents temes, explorarem cadascun dels patrons estructurals en detall, proporcionant exemples pràctics i exercicis per reforçar els conceptes apresos.

© Copyright 2024. Tots els drets reservats