---
# System prepended metadata

title: Patró de factoria abstracta
tags: [UF4 Components d'accés a dades]

---

# Patró de factoria abstracta

El patró de factoria abstracta (***Abstract Factory***) és un patró de disseny de software "creacional", és a dir, orientat a la creació d'objectes de forma flexible i reutilitzant tant com es pugui el codi (que crea aquests objectes).

Altres patrons creacionals són:
* **Factory Method** o constructor virtual.
* **Singleton**
* **Builder**

### Idea

Prenent l'exemple de **refactoring.guru** ([Abstract Factory](https://refactoring.guru/es/design-patterns/abstract-factory)), imaginem que tenim una botiga de mobles on es venen cadires, tauletes o sofàs, de tal manera que el conjunt d'aquests tres mobles conforma una família de productes. Com hi ha diferents estils de mobles, per exemple, "minimalistes", "clàssics", "art-decó", entre altres, la botiga diposarà d'aquestes variants/estils dins cada família de productes.

![image](https://hackmd.io/_uploads/H1x0RUL96.png)

Font de la imatge: [Refactoring.guru - Abstract Factory](https://refactoring.guru/es/design-patterns/abstract-factory)

Per poder satisfer les necessitats dels clients, la botiga necessita que la fàbrica de mobles sigui capaç de produir cadascun dels mobles de forma individual, de tal forma que si per exemple tinc una cadira i una tauleta "Art Decó" pugui en qualsevol moment encarregar i tenir el sofà "Art Decó" (o qualsevol moble de la mateixa variant).

Per altra banda, voldrem reaprofitar al màxim no sols el maquinari, sinó també les infraestructures de tot tipus cada cop que es produeix una nova variant o un nou tipus de moble.

Cercant un símil amb un desenvolupament de software, si fem un canvi en el disseny de l'aplicació que incorpora funcionalitat nova, voldrem reaprofitar al màxim el codi que ja tenim i no "reinventar la roda".

### Elements que intervenen en el patró Abstract Factory

En primer lloc, el que suggereix el patró Abstract Factory és que declarem explícitament les interfícies de cada producte de la família de productes, de tal forma que les variants de cada producte hauran d'implementar si o si aquesta interfície:

![image](https://hackmd.io/_uploads/By8NQvL5T.png)

Font de la imatge: [Refactoring.guru - Abstract Factory](https://refactoring.guru/es/design-patterns/abstract-factory)

En segon lloc, cal declarar el que s'anomena fàbrica (o factoria) abstracta: una altra interfície que conté els mètodes de creació dels productes de cada família (sense implementació, òbviament). Un cop implementats aquests mètodes el que faran és retornar productes "abstractes", és a dir, retornaran les interfícies de cada producte (que hem representat a dalt):

![image](https://hackmd.io/_uploads/By20VD8ca.png)

Font de la imatge: [Refactoring.guru - Abstract Factory](https://refactoring.guru/es/design-patterns/abstract-factory)

Fixem-nos que "VictorianFurnitureFactory" i "ModernFurnitureFactory" són fàbriques independents (de les variants de cada moble) basades en la interfície "FurnitureFactory".

Traslladat a un desenvolupament d'una aplicació, el codi d'aquesta ha de funcionar amb fàbriques i interfícies abstractes, de tal forma que en un moment donat pugui canviar el tipus de fàbrica (cadires, tauletes, etc.) o pugui canviar la variant (Moderna, Victoriana, etc.) sense haver-lo de descomposar i reconstruir. Tot això ha de ser transparent pel client que interacciona amb aquesta fàbrica.

Una de les coses que veurem és que en inicialitzar l'aplicació es creen els objectes de fàbrica, però per poder fer-ho s'haurà d'especificar el tipus de fàbrica. Això es farà com a part de la configuració de l'entorn.

### Estructura

* **Productes abstractes**
    * Abstract Product A i B són interfícies de productes que conformen una mateixa família (de productes). 
* **Productes concrets**
    * Concrete Product A1, A2, B1 i B2 són implementacions diferents dels productes abstractes (agrupats per varietats). 
    * Cada producte abstracte (cadira, sofà) ha de poder implementar-se en totes les seves varietats (modern, art-deco).
* **Fàbrica abstracta**
    * És una interfície que declara un grup de mètodes, els quals s'encarreguen de crear cadascun dels productes abstractes.
* **Fàbrica concreta**
    * Són les implementacions dels mètodes definits a la fàbrica abstracta. 
    * Hi ha una correspondència entre una fàbrica concreta i una varietat de producte (producte concret). Cada fàbrica concreta crea únicament productes concrets d'aquella varietat/especialitat.
* **Client**
    * El client (o aplicació) pot funcionar amb qualsevol variant de fàbrica/producte concret, sempre i quan es comuniqui amb els seus objectes mitjançant interfícies abstractes.

![image](https://hackmd.io/_uploads/Bk_uSiLcp.png)
Font de la imatge: [Refactoring.guru - Abstract Factory](https://refactoring.guru/es/design-patterns/abstract-factory)

### Consells d'implementació

D'acord a refactoring.guru, per poder aplicar aquest patró de disseny cal:

1. Tenir clar en quin cas utilitzar aquest patró: Codi que funcioni amb famílies de productes relacionades, però que no depengui d'implementacions concretes d'aquests productes.
2. Identificar les diferents variants d'un mateix producte i fer una mena de matriu de productes envers les seves variants.
3. Declarar interfícies de productes abstractes per cadascun dels diferents productes. Cal que totes les classes concretes de productes implementin aquestes interfícies.
4. Declarar la interfície de fàbrica abstracta amb els mètodes de creació dels productes abstractes (la seva signatura).
5. Implementar un grup de classes concretes de fàbrica, una per cada variant de producte.
6. Crear el codi encarregat d'inicialitzar la fàbrica (dins el main). Això consistirà en fer una instància de classe concreta de la fàbrica, de tal forma que es passa aquest objecte de fàbrica a totes les classes que construeixen productes.
7. En cas que tinguem que refactoritzar el nostre codi en base a aquest patró s'han de localitzar les crides als constructors de productes concrets i canviar-les pels mètodes de creació adequats d'acord a l'objecte de fàbrica corresponent.

### Factoria abstracta en python

Primer declarem les interfícies de cada producte abstracte. useful_function_a és qualsevol mètode que implementaran les subclasses que depenen d'aquesta interfície

NOTA IMPORTANT: Al final s'incorpora el codi complet amb tots els imports necessaris. La separació és fa per afavorir el seu enteniment.

```
class AbstractProductA(ABC):
    """
    Each distinct product of a product family should have a base interface. All
    variants of the product must implement this interface.
    """

    @abstractmethod
    def useful_function_a(self) -> str:
        pass
```

Fem el mateix amb un producte abstracte B, on podeu veure que incorporem un nou mètode que està involucrant la interfície abstracta del producte A (realment no és obligatori, però és perquè veieu que es pot fer): 

```
class AbstractProductB(ABC):
    """
    Here's the the base interface of another product. All products can interact
    with each other, but proper interaction is possible only between products of
    the same concrete variant.
    """
    @abstractmethod
    def useful_function_b(self) -> None:
        """
        Product B is able to do its own thing...
        """
        pass

    @abstractmethod
    def another_useful_function_b(self, collaborator: AbstractProductA) -> None:
        """
        ...but it also can collaborate with the ProductA.

        The Abstract Factory makes sure that all products it creates are of the
        same variant and thus, compatible.
        """
        pass
```

Anem amb les classes concretes. Comencem per les d'A i seguim per les de B:

```
"""
Concrete Products are created by corresponding Concrete Factories.
"""

class ConcreteProductA1(AbstractProductA):
    def useful_function_a(self) -> str:
        return "The result of the product A1."
    
class ConcreteProductA2(AbstractProductA):
    def useful_function_a(self) -> str:
        return "The result of the product A2."
```

```
class ConcreteProductB1(AbstractProductB):
    def useful_function_b(self) -> str:
        return "The result of the product B1."

    """
    The variant, Product B1, is only able to work correctly with the variant,
    Product A1. Nevertheless, it accepts any instance of AbstractProductA as an
    argument.
    """

    def another_useful_function_b(self, collaborator: AbstractProductA) -> str:
        result = collaborator.useful_function_a()
        return f"The result of the B1 collaborating with the ({result})"


class ConcreteProductB2(AbstractProductB):
    def useful_function_b(self) -> str:
        return "The result of the product B2."

    def another_useful_function_b(self, collaborator: AbstractProductA):
        """
        The variant, Product B2, is only able to work correctly with the
        variant, Product A2. Nevertheless, it accepts any instance of
        AbstractProductA as an argument.
        """
        result = collaborator.useful_function_a()
        return f"The result of the B2 collaborating with the ({result})"
```

Seguint l'ordre, declarem l'Abstract Factory:

```
class AbstractFactory(ABC):
    """
    The Abstract Factory interface declares a set of methods that return
    different abstract products. These products are called a family and are
    related by a high-level theme or concept. Products of one family are usually
    able to collaborate among themselves. A family of products may have several
    variants, but the products of one variant are incompatible with products of
    another.
    """
    @abstractmethod
    def create_product_a(self) -> AbstractProductA:
        pass

    @abstractmethod
    def create_product_b(self) -> AbstractProductB:
        pass
```

I ja per últim les implementacions concretes en fàbriques concretes:

```
from abc import ABC, abstractmethod
import AbstractFactory
import AbstractProductA
import AbstractProductB
from ConcreteProductA import ConcreteProductA1, ConcreteProductA2
from ConcreteProductB import ConcreteProductB1, ConcreteProductB2

class ConcreteFactory1(AbstractFactory):
    """
    Concrete Factories produce a family of products that belong to a single
    variant. The factory guarantees that resulting products are compatible. Note
    that signatures of the Concrete Factory's methods return an abstract
    product, while inside the method a concrete product is instantiated.
    """

    def create_product_a(self) -> AbstractProductA:
        return ConcreteProductA1()

    def create_product_b(self) -> AbstractProductB:
        return ConcreteProductB1()


class ConcreteFactory2(AbstractFactory):
    """
    Each Concrete Factory has a corresponding product variant.
    """

    def create_product_a(self) -> AbstractProductA:
        return ConcreteProductA2()

    def create_product_b(self) -> AbstractProductB:
        return ConcreteProductB2()
```

I ara, el codi client:

```
def client_code(factory: AbstractFactory) -> None:
    """
    The client code works with factories and products only through abstract
    types: AbstractFactory and AbstractProduct. This lets you pass any factory
    or product subclass to the client code without breaking it.
    """
    product_a = factory.create_product_a()
    product_b = factory.create_product_b()

    print(f"{product_b.useful_function_b()}")
    print(f"{product_b.another_useful_function_b(product_a)}", end="")


if __name__ == "__main__":
    """
    The client code can work with any concrete factory class.
    """
    print("Client: Testing client code with the first factory type:")
    client_code(ConcreteFactory1())

    print("\n")

    print("Client: Testing the same client code with the second factory type:")
    client_code(ConcreteFactory2())
```

El codi complet amb tots els imports necessaris és:

```
from __future__ import annotations
from abc import ABC, abstractmethod


class AbstractFactory(ABC):
    """
    The Abstract Factory interface declares a set of methods that return
    different abstract products. These products are called a family and are
    related by a high-level theme or concept. Products of one family are usually
    able to collaborate among themselves. A family of products may have several
    variants, but the products of one variant are incompatible with products of
    another.
    """
    @abstractmethod
    def create_product_a(self) -> AbstractProductA:
        pass

    @abstractmethod
    def create_product_b(self) -> AbstractProductB:
        pass


class ConcreteFactory1(AbstractFactory):
    """
    Concrete Factories produce a family of products that belong to a single
    variant. The factory guarantees that resulting products are compatible. Note
    that signatures of the Concrete Factory's methods return an abstract
    product, while inside the method a concrete product is instantiated.
    """

    def create_product_a(self) -> AbstractProductA:
        return ConcreteProductA1()

    def create_product_b(self) -> AbstractProductB:
        return ConcreteProductB1()


class ConcreteFactory2(AbstractFactory):
    """
    Each Concrete Factory has a corresponding product variant.
    """

    def create_product_a(self) -> AbstractProductA:
        return ConcreteProductA2()

    def create_product_b(self) -> AbstractProductB:
        return ConcreteProductB2()


class AbstractProductA(ABC):
    """
    Each distinct product of a product family should have a base interface. All
    variants of the product must implement this interface.
    """

    @abstractmethod
    def useful_function_a(self) -> str:
        pass


"""
Concrete Products are created by corresponding Concrete Factories.
"""


class ConcreteProductA1(AbstractProductA):
    def useful_function_a(self) -> str:
        return "The result of the product A1."


class ConcreteProductA2(AbstractProductA):
    def useful_function_a(self) -> str:
        return "The result of the product A2."


class AbstractProductB(ABC):
    """
    Here's the the base interface of another product. All products can interact
    with each other, but proper interaction is possible only between products of
    the same concrete variant.
    """
    @abstractmethod
    def useful_function_b(self) -> None:
        """
        Product B is able to do its own thing...
        """
        pass

    @abstractmethod
    def another_useful_function_b(self, collaborator: AbstractProductA) -> None:
        """
        ...but it also can collaborate with the ProductA.

        The Abstract Factory makes sure that all products it creates are of the
        same variant and thus, compatible.
        """
        pass


"""
Concrete Products are created by corresponding Concrete Factories.
"""


class ConcreteProductB1(AbstractProductB):
    def useful_function_b(self) -> str:
        return "The result of the product B1."

    """
    The variant, Product B1, is only able to work correctly with the variant,
    Product A1. Nevertheless, it accepts any instance of AbstractProductA as an
    argument.
    """

    def another_useful_function_b(self, collaborator: AbstractProductA) -> str:
        result = collaborator.useful_function_a()
        return f"The result of the B1 collaborating with the ({result})"


class ConcreteProductB2(AbstractProductB):
    def useful_function_b(self) -> str:
        return "The result of the product B2."

    def another_useful_function_b(self, collaborator: AbstractProductA):
        """
        The variant, Product B2, is only able to work correctly with the
        variant, Product A2. Nevertheless, it accepts any instance of
        AbstractProductA as an argument.
        """
        result = collaborator.useful_function_a()
        return f"The result of the B2 collaborating with the ({result})"


def client_code(factory: AbstractFactory) -> None:
    """
    The client code works with factories and products only through abstract
    types: AbstractFactory and AbstractProduct. This lets you pass any factory
    or product subclass to the client code without breaking it.
    """
    product_a = factory.create_product_a()
    product_b = factory.create_product_b()

    print(f"{product_b.useful_function_b()}")
    print(f"{product_b.another_useful_function_b(product_a)}", end="")


if __name__ == "__main__":
    """
    The client code can work with any concrete factory class.
    """
    print("Client: Testing client code with the first factory type:")
    client_code(ConcreteFactory1())

    print("\n")

    print("Client: Testing the same client code with the second factory type:")
    client_code(ConcreteFactory2())
```