Introducció

El patró Abstract Factory és un patró creacional que proporciona una interfície per crear famílies d'objectes relacionats o dependents sense especificar les seves classes concretes. Aquest patró és útil quan el sistema ha de ser independent de com es creen, componen i representen els seus objectes.

Objectius del Patró Abstract Factory

  • Proporcionar una interfície per crear famílies d'objectes relacionats.
  • Permetre que els clients utilitzin objectes sense conèixer les seves classes concretes.
  • Facilitar l'intercanvi d'objectes fàcilment.

Estructura

L'estructura del patró Abstract Factory inclou els següents components:

  1. AbstractFactory: Declara una interfície per a les operacions que creen objectes abstractes.
  2. ConcreteFactory: Implementa les operacions per crear objectes concrets.
  3. AbstractProduct: Declara una interfície per a un tipus d'objecte producte.
  4. ConcreteProduct: Defineix un objecte producte que serà creat per la fàbrica concreta corresponent.
  5. Client: Utilitza només les interfícies declarades per AbstractFactory i AbstractProduct.

Diagrama UML

+------------------+          +------------------+
|  AbstractFactory |<-------->|  ConcreteFactory |
+------------------+          +------------------+
| +createProductA()|          | +createProductA()|
| +createProductB()|          | +createProductB()|
+------------------+          +------------------+
        ^                           ^
        |                           |
        |                           |
+------------------+          +------------------+
|  AbstractProductA|          |  ConcreteProductA|
+------------------+          +------------------+
| +operation()     |          | +operation()     |
+------------------+          +------------------+
        ^                           ^
        |                           |
        |                           |
+------------------+          +------------------+
|  AbstractProductB|          |  ConcreteProductB|
+------------------+          +------------------+
| +operation()     |          | +operation()     |
+------------------+          +------------------+

Exemple Pràctic

Escenari

Suposem que estem desenvolupant una aplicació que ha de funcionar en diferents sistemes operatius (Windows i Mac). Necessitem crear interfícies d'usuari específiques per a cada sistema operatiu.

Implementació

1. Definició de les interfícies abstractes

// Abstract Factory
public interface GUIFactory {
    Button createButton();
    Checkbox createCheckbox();
}

// Abstract Product A
public interface Button {
    void paint();
}

// Abstract Product B
public interface Checkbox {
    void paint();
}

2. Implementació de les fàbriques concretes

// Concrete Factory 1
public class WindowsFactory implements GUIFactory {
    @Override
    public Button createButton() {
        return new WindowsButton();
    }

    @Override
    public Checkbox createCheckbox() {
        return new WindowsCheckbox();
    }
}

// Concrete Factory 2
public class MacFactory implements GUIFactory {
    @Override
    public Button createButton() {
        return new MacButton();
    }

    @Override
    public Checkbox createCheckbox() {
        return new MacCheckbox();
    }
}

3. Implementació dels productes concrets

// Concrete Product A1
public class WindowsButton implements Button {
    @Override
    public void paint() {
        System.out.println("Rendering a button in Windows style.");
    }
}

// Concrete Product A2
public class MacButton implements Button {
    @Override
    public void paint() {
        System.out.println("Rendering a button in Mac style.");
    }
}

// Concrete Product B1
public class WindowsCheckbox implements Checkbox {
    @Override
    public void paint() {
        System.out.println("Rendering a checkbox in Windows style.");
    }
}

// Concrete Product B2
public class MacCheckbox implements Checkbox {
    @Override
    public void paint() {
        System.out.println("Rendering a checkbox in Mac style.");
    }
}

4. Utilització del patró Abstract Factory

public class Application {
    private Button button;
    private Checkbox checkbox;

    public Application(GUIFactory factory) {
        button = factory.createButton();
        checkbox = factory.createCheckbox();
    }

    public void paint() {
        button.paint();
        checkbox.paint();
    }
}

public class Demo {
    private static Application configureApplication() {
        Application app;
        GUIFactory factory;
        String osName = System.getProperty("os.name").toLowerCase();
        if (osName.contains("mac")) {
            factory = new MacFactory();
        } else {
            factory = new WindowsFactory();
        }
        app = new Application(factory);
        return app;
    }

    public static void main(String[] args) {
        Application app = configureApplication();
        app.paint();
    }
}

Exercicis Pràctics

Exercici 1: Implementació d'una Fàbrica per a Linux

  1. Descripció: Implementa una fàbrica concreta per a Linux que creï botons i caselles de verificació en estil Linux.
  2. Passos:
    • Crea les classes LinuxFactory, LinuxButton i LinuxCheckbox.
    • Implementa les interfícies GUIFactory, Button i Checkbox respectivament.

Solució

// Concrete Factory 3
public class LinuxFactory implements GUIFactory {
    @Override
    public Button createButton() {
        return new LinuxButton();
    }

    @Override
    public Checkbox createCheckbox() {
        return new LinuxCheckbox();
    }
}

// Concrete Product A3
public class LinuxButton implements Button {
    @Override
    public void paint() {
        System.out.println("Rendering a button in Linux style.");
    }
}

// Concrete Product B3
public class LinuxCheckbox implements Checkbox {
    @Override
    public void paint() {
        System.out.println("Rendering a checkbox in Linux style.");
    }
}

Exercici 2: Afegir un Nou Tipus de Producte

  1. Descripció: Afegiu un nou tipus de producte, TextField, a les fàbriques existents.
  2. Passos:
    • Defineix la interfície TextField.
    • Implementa TextField per a Windows, Mac i Linux.
    • Actualitza les fàbriques per crear TextField.

Solució

// Abstract Product C
public interface TextField {
    void render();
}

// Concrete Product C1
public class WindowsTextField implements TextField {
    @Override
    public void render() {
        System.out.println("Rendering a text field in Windows style.");
    }
}

// Concrete Product C2
public class MacTextField implements TextField {
    @Override
    public void render() {
        System.out.println("Rendering a text field in Mac style.");
    }
}

// Concrete Product C3
public class LinuxTextField implements TextField {
    @Override
    public void render() {
        System.out.println("Rendering a text field in Linux style.");
    }
}

// Update Factories
public interface GUIFactory {
    Button createButton();
    Checkbox createCheckbox();
    TextField createTextField(); // New method
}

public class WindowsFactory implements GUIFactory {
    @Override
    public Button createButton() {
        return new WindowsButton();
    }

    @Override
    public Checkbox createCheckbox() {
        return new WindowsCheckbox();
    }

    @Override
    public TextField createTextField() {
        return new WindowsTextField();
    }
}

public class MacFactory implements GUIFactory {
    @Override
    public Button createButton() {
        return new MacButton();
    }

    @Override
    public Checkbox createCheckbox() {
        return new MacCheckbox();
    }

    @Override
    public TextField createTextField() {
        return new MacTextField();
    }
}

public class LinuxFactory implements GUIFactory {
    @Override
    public Button createButton() {
        return new LinuxButton();
    }

    @Override
    public Checkbox createCheckbox() {
        return new LinuxCheckbox();
    }

    @Override
    public TextField createTextField() {
        return new LinuxTextField();
    }
}

Resum

El patró Abstract Factory és una eina poderosa per crear famílies d'objectes relacionats sense dependre de les seves classes concretes. Aquest patró és especialment útil quan es treballa amb sistemes que han de ser independents de la plataforma o quan es necessita intercanviar fàcilment diferents implementacions d'objectes. Hem vist com implementar aquest patró amb un exemple pràctic i hem proporcionat exercicis per reforçar els conceptes apresos.

© Copyright 2024. Tots els drets reservats