desarrollo

El patrón Abstract Factory

Javier Sanchez ToledanoJavier Sanchez Toledano Twitter
El patrón Abstract Factory
Comparte

Continuando con la serie de patrones de diseño, presentamos ahora el Abstract Factory.

El patrón de diseño Abstract Factory es similar al Factory Method, pero en lugar de crear objetos individuales, crea familias completas de objetos relacionados. En otras palabras, el patrón Abstract Factory proporciona una interfaz para crear una familia de objetos relacionados o dependientes sin especificar sus clases concretas.

La implementación del patrón Abstract Factory implica crear una interfaz o clase abstracta que declara métodos para crear objetos de cada familia. Luego se crean las clases concretas que implementan esta interfaz o clase abstracta y proporcionan su propia implementación de los métodos de fábrica para crear los objetos. Finalmente, se utiliza la fábrica para crear los objetos.

Por ejemplo, supongamos que se está desarrollando un juego de rol y se necesita crear personajes y objetos relacionados con el juego, como armas, armaduras y pociones. Estos objetos pueden ser agrupados en familias relacionadas, como objetos de guerrero y objetos de mago. En lugar de crear cada objeto individualmente, se puede utilizar el patrón Abstract Factory para crear todas las familias de objetos de una sola vez.

Ejemplos

A continuación se muestra un ejemplo de cómo se puede implementar el patrón Abstract Factory en diferentes lenguajes de programación:

Java

// Interfaz abstracta para crear familias de objetos relacionados
public interface AbstractFactory {
    public Weapon createWeapon();
    public Armor createArmor();
    public Potion createPotion();
}

// Implementación concreta de la interfaz AbstractFactory para la familia de objetos de guerrero
public class WarriorFactory implements AbstractFactory {
    public Weapon createWeapon() {
        return new Sword();
    }

    public Armor createArmor() {
        return new PlateArmor();
    }

    public Potion createPotion() {
        return new HealthPotion();
    }
}

// Implementación concreta de la interfaz AbstractFactory para la familia de objetos de mago
public class MageFactory implements AbstractFactory {
    public Weapon createWeapon() {
        return new Staff();
    }

    public Armor createArmor() {
        return new Robe();
    }

    public Potion createPotion() {
        return new ManaPotion();
    }
}

Ejemplo en Python

# Interfaz abstracta para crear familias de objetos relacionados
class AbstractFactory:
    def create_weapon(self):
        pass

    def create_armor(self):
        pass

    def create_potion(self):
        pass

# Implementación concreta de la interfaz AbstractFactory para la familia de objetos de guerrero
class WarriorFactory(AbstractFactory):
    def create_weapon(self):
        return Sword()

    def create_armor(self):
        return PlateArmor()

    def create_potion(self):
        return HealthPotion()

# Implementación concreta de la interfaz AbstractFactory para la familia de objetos de mago
class MageFactory(AbstractFactory):
    def create_weapon(self):
        return Staff()

    def create_armor(self):
        return Robe()

    def create_potion(self):
        return ManaPotion()

Ejemplo en Go

Siguiendo con el ejemplo en Go, la implementación del patrón Abstract Factory en este lenguaje podría lucir de la siguiente manera:

package main

import "fmt"

// Interfaz para la fabricación de productos abstractos
type AbstractFactory interface {
    createProductA() AbstractProductA
    createProductB() AbstractProductB
}

// Interfaz para los productos A abstractos
type AbstractProductA interface {
    getInfo() string
}

// Interfaz para los productos B abstractos
type AbstractProductB interface {
    getInfo() string
}

// Concrete Factory 1
type ConcreteFactory1 struct {}

func (cf ConcreteFactory1) createProductA() AbstractProductA {
    return ConcreteProductA1{}
}

func (cf ConcreteFactory1) createProductB() AbstractProductB {
    return ConcreteProductB1{}
}

// Concrete Factory 2
type ConcreteFactory2 struct {}

func (cf ConcreteFactory2) createProductA() AbstractProductA {
    return ConcreteProductA2{}
}

func (cf ConcreteFactory2) createProductB() AbstractProductB {
    return ConcreteProductB2{}
}

// Concrete Product A1
type ConcreteProductA1 struct {}

func (cpa ConcreteProductA1) getInfo() string {
    return "Soy el producto A1 creado por la Concrete Factory 1"
}

// Concrete Product A2
type ConcreteProductA2 struct {}

func (cpa ConcreteProductA2) getInfo() string {
    return "Soy el producto A2 creado por la Concrete Factory 2"
}

// Concrete Product B1
type ConcreteProductB1 struct {}

func (cpb ConcreteProductB1) getInfo() string {
    return "Soy el producto B1 creado por la Concrete Factory 1"
}

// Concrete Product B2
type ConcreteProductB2 struct {}

func (cpb ConcreteProductB2) getInfo() string {
    return "Soy el producto B2 creado por la Concrete Factory 2"
}

func main() {
    // Usando Concrete Factory 1
    cf1 := ConcreteFactory1{}
    pa1 := cf1.createProductA()
    pb1 := cf1.createProductB()
    fmt.Println(pa1.getInfo())
    fmt.Println(pb1.getInfo())

    // Usando Concrete Factory 2
    cf2 := ConcreteFactory2{}
    pa2 := cf2.createProductA()
    pb2 := cf2.createProductB()
    fmt.Println(pa2.getInfo())
    fmt.Println(pb2.getInfo())
}

Ejemplo en CSharp

using System;

// Interfaz abstracta del producto A
public interface IAbstractProductA
{
    string GetName();
}

// Interfaz abstracta del producto B
public interface IAbstractProductB
{
    string GetName();
}

// Implementación concreta del producto A
public class ConcreteProductA1 : IAbstractProductA
{
    public string GetName()
    {
        return "Product A1";
    }
}

// Implementación concreta del producto B
public class ConcreteProductB1 : IAbstractProductB
{
    public string GetName()
    {
        return "Product B1";
    }
}

// Implementación concreta del producto A
public class ConcreteProductA2 : IAbstractProductA
{
    public string GetName()
    {
        return "Product A2";
    }
}

// Implementación concreta del producto B
public class ConcreteProductB2 : IAbstractProductB
{
    public string GetName()
    {
        return "Product B2";
    }
}

// Interfaz abstracta de la fábrica abstracta
public interface IAbstractFactory
{
    IAbstractProductA CreateProductA();
    IAbstractProductB CreateProductB();
}

// Implementación concreta de la fábrica abstracta 1
public class ConcreteFactory1 : IAbstractFactory
{
    public IAbstractProductA CreateProductA()
    {
        return new ConcreteProductA1();
    }

    public IAbstractProductB CreateProductB()
    {
        return new ConcreteProductB1();
    }
}

// Implementación concreta de la fábrica abstracta 2
public class ConcreteFactory2 : IAbstractFactory
{
    public IAbstractProductA CreateProductA()
    {
        return new ConcreteProductA2();
    }

    public IAbstractProductB CreateProductB()
    {
        return new ConcreteProductB2();
    }
}

// Clase cliente
public class Client
{
    private IAbstractProductA _productA;
    private IAbstractProductB _productB;

    public Client(IAbstractFactory factory)
    {
        _productA = factory.CreateProductA();
        _productB = factory.CreateProductB();
    }

    public void Run()
    {
        Console.WriteLine(_productA.GetName());
        Console.WriteLine(_productB.GetName());
    }
}

// Clase de entrada
class Program
{
    static void Main(string[] args)
    {
        // Creamos una instancia de la fábrica abstracta 1
        IAbstractFactory factory1 = new ConcreteFactory1();

        // Creamos una instancia del cliente y lo ejecutamos con la fábrica 1
        Client client1 = new Client(factory1);
        client1.Run();

        // Creamos una instancia de la fábrica abstracta 2
        IAbstractFactory factory2 = new ConcreteFactory2();

        // Creamos una instancia del cliente y lo ejecutamos con la fábrica 2
        Client client2 = new Client(factory2);
        client2.Run();
    }
}

En este ejemplo, se definen las interfaces y clases abstractas IAbstractProductA, IAbstractProductB e IAbstractFactory, que son implementadas por las clases concretas ConcreteProductA1, ConcreteProductB1, ConcreteProductA2, ConcreteProductB2, ConcreteFactory1 y ConcreteFactory2. Luego se crea la clase Client que utiliza las fábricas concretas para crear y trabajar.

Beneficios

Después de haber utilizado el patrón Abstract Factory, se pueden obtener varios beneficios. Entre ellos, destacan:

  • Modularidad
    el patrón permite encapsular la creación de objetos, evitando que esta responsabilidad esté dispersa en diferentes partes del código y fomentando una mayor modularidad.
  • Flexibilidad
    la capacidad de cambiar entre diferentes familias de objetos, simplemente cambiando la fábrica concreta utilizada, hace que el patrón sea muy flexible y fácil de adaptar a diferentes situaciones.
  • Extensibilidad
    el patrón Abstract Factory facilita la adición de nuevas familias de objetos sin afectar el código existente, simplemente agregando nuevas clases concretas.

En conclusión, el patrón Abstract Factory es una solución elegante para crear familias de objetos relacionados sin especificar sus clases concretas. Proporciona una forma de encapsular la creación de objetos, promueve la modularidad, la flexibilidad y la extensibilidad del código, lo que puede resultar muy beneficioso en proyectos complejos y de gran escala.

Javier Sanchez Toledano
Escrito por Javier Sanchez Toledano Sígueme

Soy Licenciado en Informática e Ingeniero en Sistemas Computacionales.
Soy auditor líder certificado por ICA en la Norma ISO 9000, desarrollo sistemas de gestión de la calidad con un enfoque de mejora continua, creo tableros de control con indicadores clave para mejorar la toma de decisiones basadas en datos.

Comentarios