CodeGym/Blog Java/Random-ES/Patrones de diseño: método de fábrica
Autor
Andrey Gorkovenko
Frontend Engineer at NFON AG

Patrones de diseño: método de fábrica

Publicado en el grupo Random-ES
¡Hola! Hoy continuaremos estudiando patrones de diseño y discutiremos el patrón del método de fábrica. Patrones de diseño: método de fábrica - 1 Descubrirá qué es y para qué tareas es adecuado este patrón. Consideraremos este patrón de diseño en la práctica y estudiaremos su estructura. Para asegurarse de que todo esté claro, debe comprender los siguientes temas:
  1. Herencia en Java.
  2. Clases y métodos abstractos en Java

¿Qué problema resuelve el método de fábrica?

Todos los patrones de diseño de fábricas tienen dos tipos de participantes: creadores (las propias fábricas) y productos (los objetos creados por las fábricas). Imagine la siguiente situación: tenemos una fábrica que produce automóviles con la marca CodeGym. Sabe cómo crear modelos de automóviles con varios tipos de carrocerías:
  • sedanes
  • camionetas
  • cupés
Nuestro negocio prosperó tanto que un buen día adquirimos otro fabricante de automóviles: OneAuto. Siendo empresarios sensatos, no queremos perder ningún cliente de OneAuto, por lo que nos enfrentamos a la tarea de reestructurar la producción para poder producir:
  • sedán CodeGym
  • Furgonetas CodeGym
  • CodeGym cupés
  • sedán OneAuto
  • Furgonetas OneAuto
  • Cupés OneAuto
Como puede ver, en lugar de un grupo de productos, ahora tenemos dos, y difieren en ciertos detalles. El patrón de diseño del método de fábrica es para cuando necesitamos crear diferentes grupos de productos, cada uno de los cuales tiene algunas características específicas. Consideraremos el principio rector de este patrón en la práctica, pasando gradualmente de lo simple a lo complejo, utilizando el ejemplo de nuestra cafetería, que creamos en una de las lecciones anteriores .

Un poco sobre el patrón de fábrica.

Permítanme recordarles que anteriormente construimos una pequeña cafetería virtual. Con la ayuda de una sencilla fábrica, aprendimos a crear diferentes tipos de café. Hoy volveremos a trabajar este ejemplo. Recordemos cómo se veía nuestra cafetería, con su sencilla fábrica. Tuvimos una clase de café:
public class Coffee {
    public void grindCoffee(){
        // Grind the coffee
    }
    public void makeCoffee(){
        // Brew the coffee
    }
    public void pourIntoCup(){
        // Pour into a cup
    }
}
Y varias clases secundarias correspondientes a tipos específicos de café que nuestra fábrica podría producir:
public class Americano extends Coffee {}
public class Cappuccino extends Coffee {}
public class CaffeLatte extends Coffee {}
public class Espresso extends Coffee {}
Creamos una enumeración para facilitar la realización de pedidos:
public enum CoffeeType {
    ESPRESSO,
    AMERICANO,
    CAFFE_LATTE,
    CAPPUCCINO
}
La fábrica de café en sí se veía así:
public class SimpleCoffeeFactory {
    public Coffee createCoffee(CoffeeType type) {
        Coffee coffee = null;

        switch (type) {
            case AMERICANO:
                coffee = new Americano();
                break;
            case ESPRESSO:
                coffee = new Espresso();
                break;
            case CAPPUCCINO:
                coffee = new Cappuccino();
                break;
            case CAFFE_LATTE:
                coffee = new CaffeLatte();
                break;
        }

        return coffee;
    }
}
Y finalmente, la cafetería en sí se veía así:
public class CoffeeShop {

    private final SimpleCoffeeFactory coffeeFactory;

    public CoffeeShop(SimpleCoffeeFactory coffeeFactory) {
        this.coffeeFactory = coffeeFactory;
    }

    public Coffee orderCoffee(CoffeeType type) {
        Coffee coffee = coffeeFactory.createCoffee(type);
        coffee.grindCoffee();
        coffee.makeCoffee();
        coffee.pourIntoCup();

        System.out.println("Here's your coffee! Thanks! Come again!");
        return coffee;
    }
}

Modernización de una fábrica sencilla

Nuestra cafetería está funcionando muy bien. Tanto es así que estamos pensando en ampliar. Queremos abrir algunas ubicaciones nuevas. Somos audaces y emprendedores, por lo que no crearemos cafeterías aburridas. Queremos que cada tienda tenga un toque especial. En consecuencia, para empezar, abriremos dos ubicaciones: una italiana y otra americana. Estos cambios afectarán no solo al diseño interior, sino también a las bebidas que se ofrecen:
  • en la cafetería italiana utilizaremos exclusivamente marcas de café italiano, con molienda y tostado especiales.
  • la ubicación estadounidense tendrá porciones más grandes y serviremos malvaviscos con cada pedido.
Lo único que permanece invariable es nuestro modelo de negocio, que ha demostrado ser excelente. En términos del código, esto es lo que sucede. Teníamos 4 clases correspondientes a nuestros productos:
public class Americano extends Coffee {}
public class Cappuccino extends Coffee {}
public class CaffeLatte extends Coffee {}
public class Espresso extends Coffee {}
Pero ahora tendremos 8:
public class ItalianStyleAmericano extends Coffee {}
public class ItalianStyleCappucino extends Coffee {}
public class ItalianStyleCaffeLatte extends Coffee {}
public class ItalianStyleEspresso extends Coffee {}

public class AmericanStyleAmericano extends Coffee {}
public class AmericanStyleCappucino extends Coffee {}
public class AmericanStyleCaffeLatte extends Coffee {}
public class AmericanStyleEspresso extends Coffee {}
Dado que queremos mantener el modelo comercial actual, queremos que el orderCoffee(CoffeeType type)método sufra la menor cantidad de cambios posible. Mira esto:
public Coffee orderCoffee(CoffeeType type) {
    Coffee coffee = coffeeFactory.createCoffee(type);
    coffee.grindCoffee();
    coffee.makeCoffee();
    coffee.pourIntoCup();

    System.out.println("Here's your coffee! Thanks! Come again!");
    return coffee;
}
¿Qué opciones tenemos? Bueno, ya sabemos cómo escribir una fábrica, ¿verdad? Lo más simple que viene a la mente de inmediato es escribir dos fábricas similares y luego pasar la implementación deseada al constructor de nuestra cafetería. Al hacer esto, la clase de la cafetería no cambiará. Primero, necesitamos crear una nueva clase de fábrica, hacer que herede nuestra fábrica simple y luego anular el createCoffee(CoffeeType type)método. Escribamos fábricas para crear café estilo italiano y café estilo americano:
public class SimpleItalianCoffeeFactory extends SimpleCoffeeFactory {

    @Override
    public Coffee createCoffee(CoffeeType type) {
        Coffee coffee = null;
        switch (type) {
            case AMERICANO:
                coffee = new ItalianStyleAmericano();
                break;
            case ESPRESSO:
                coffee = new ItalianStyleEspresso();
                break;
            case CAPPUCCINO:
                coffee = new ItalianStyleCappuccino();
                break;
            case CAFFE_LATTE:
                coffee = new ItalianStyleCaffeLatte();
                break;
        }
        return coffee;
    }
}

public class SimpleAmericanCoffeeFactory extends SimpleCoffeeFactory{

    @Override
    public Coffee createCoffee (CoffeeType type) {
        Coffee coffee = null;

        switch (type) {
            case AMERICANO:
                coffee = new AmericanStyleAmericano();
                break;
            case ESPRESSO:
                coffee = new AmericanStyleEspresso();
                break;
            case CAPPUCCINO:
                coffee = new AmericanStyleCappuccino();
                break;
            case CAFFE_LATTE:
                coffee = new AmericanStyleCaffeLatte();
                break;
        }

        return coffee;
    }

}
Ahora podemos pasar la implementación de fábrica deseada a CoffeeShop. Veamos cómo sería el código para pedir café en diferentes cafeterías. Por ejemplo, capuchino estilo italiano y estilo americano:
public class Main {
    public static void main(String[] args) {
        /*
            Order an Italian-style cappuccino:
            1. Create a factory for making Italian coffee
            2. Create a new coffee shop, passing the Italian coffee factory to it through the constructor
            3. Order our coffee
         */
        SimpleItalianCoffeeFactory italianCoffeeFactory = new SimpleItalianCoffeeFactory();
        CoffeeShop italianCoffeeShop = new CoffeeShop(italianCoffeeFactory);
        italianCoffeeShop.orderCoffee(CoffeeType.CAPPUCCINO);


         /*
            Order an American-style cappuccino
            1. Create a factory for making American coffee
            2. Create a new coffee shop, passing the American coffee factory to it through the constructor
            3. Order our coffee
         */
        SimpleAmericanCoffeeFactory americanCoffeeFactory = new SimpleAmericanCoffeeFactory();
        CoffeeShop americanCoffeeShop = new CoffeeShop(americanCoffeeFactory);
        americanCoffeeShop.orderCoffee(CoffeeType.CAPPUCCINO);
    }
}
Creamos dos cafeterías diferentes, pasando la fábrica deseada a cada una. Por un lado, hemos logrado nuestro objetivo, pero por otro lado... De alguna manera esto no les sienta bien a los empresarios... Averigüemos qué es lo que está mal. Primero, la abundancia de fábricas. ¿Qué? Ahora, para cada nueva ubicación, se supone que debemos crear su propia fábrica y, además de eso, asegurarnos de que la fábrica relevante se pase al constructor al crear una cafetería. En segundo lugar, sigue siendo una simple fábrica. Recién modernizado ligeramente. Pero estamos aquí para aprender un nuevo patrón. En tercer lugar, ¿no es posible un enfoque diferente? Sería genial si pudiéramos poner todos los temas relacionados con la preparación del café en elCoffeeShopclase al vincular los procesos de creación de café y pedidos de servicio, al mismo tiempo que mantiene la flexibilidad suficiente para hacer varios estilos de café. La respuesta es sí, podemos. Esto se llama el patrón de diseño del método de fábrica.

De una simple fábrica a un método de fábrica

Para resolver la tarea de la manera más eficiente posible:
  1. Devolvemos el createCoffee(CoffeeType type)método a la CoffeeShopclase.
  2. Haremos este método abstracto.
  3. La CoffeeShopclase misma se volverá abstracta.
  4. La CoffeeShopclase tendrá clases para niños.
Si amigo. La cafetería italiana no es más que un descendiente de la CoffeeShopclase, que implementa el createCoffee(CoffeeType type)método de acuerdo con las mejores tradiciones de los baristas italianos. Ahora, un paso a la vez. Paso 1. Haz que la Coffeeclase sea abstracta. Disponemos de dos familias completas de productos diferentes. Aún así, los cafés italiano y estadounidense tienen un ancestro común: la Coffeeclase. Sería adecuado hacerlo abstracto:
public abstract class Coffee {
    public void makeCoffee(){
        // Brew the coffee
    }
    public void pourIntoCup(){
        // Pour into a cup
    }
}
Paso 2. Hacer CoffeeShopabstracto, con un createCoffee(CoffeeType type)método abstracto
public abstract class CoffeeShop {

    public Coffee orderCoffee(CoffeeType type) {
        Coffee coffee = createCoffee(type);

        coffee.makeCoffee();
        coffee.pourIntoCup();

        System.out.println("Here's your coffee! Thanks! Come again!");
        return coffee;
    }

    protected abstract Coffee createCoffee(CoffeeType type);
}
Paso 3. Cree una cafetería italiana, que es descendiente de la cafetería abstracta. Implementamos el createCoffee(CoffeeType type)método en él, teniendo en cuenta los detalles de las recetas italianas.
public class ItalianCoffeeShop extends CoffeeShop {

    @Override
    public Coffee createCoffee (CoffeeType type) {
        Coffee coffee = null;
        switch (type) {
            case AMERICANO:
                coffee = new ItalianStyleAmericano();
                break;
            case ESPRESSO:
                coffee = new ItalianStyleEspresso();
                break;
            case CAPPUCCINO:
                coffee = new ItalianStyleCappuccino();
                break;
            case CAFFE_LATTE:
                coffee = new ItalianStyleCaffeLatte();
                break;
        }
        return coffee;
    }
}
Paso 4. Hacemos lo mismo para la cafetería estilo americano
public class AmericanCoffeeShop extends CoffeeShop {
    @Override
    public Coffee createCoffee(CoffeeType type) {
        Coffee coffee = null;

        switch (type) {
            case AMERICANO:
                coffee = new AmericanStyleAmericano();
                break;
            case ESPRESSO:
                coffee = new AmericanStyleEspresso();
                break;
            case CAPPUCCINO:
                coffee = new AmericanStyleCappuccino();
                break;
            case CAFFE_LATTE:
                coffee = new AmericanStyleCaffeLatte();
                break;
        }

        return coffee;
    }
}
Paso 5. Mira cómo se verán los lattes americanos e italianos:
public class Main {
    public static void main(String[] args) {
        CoffeeShop italianCoffeeShop = new ItalianCoffeeShop();
        italianCoffeeShop.orderCoffee(CoffeeType.CAFFE_LATTE);

        CoffeeShop americanCoffeeShop = new AmericanCoffeeShop();
        americanCoffeeShop.orderCoffee(CoffeeType.CAFFE_LATTE);
    }
}
Felicidades. Acabamos de implementar el patrón de diseño del método de fábrica usando nuestra cafetería como ejemplo.

El principio detrás de los métodos de fábrica

Ahora consideremos con mayor detalle lo que obtuvimos. El siguiente diagrama muestra las clases resultantes. Los bloques verdes son clases de creadores y los bloques azules son clases de productos. Patrones de diseño: método de fábrica - 2¿Qué conclusiones podemos sacar?
  1. Todos los productos son implementaciones de la Coffeeclase abstracta.
  2. Todos los creadores son implementaciones de la CoffeeShopclase abstracta.
  3. Vemos dos jerarquías de clases paralelas:
    • Jerarquía de productos. Vemos descendientes de italianos y descendientes de estadounidenses.
    • Jerarquía de creadores. Vemos descendientes de italianos y descendientes de estadounidenses.
  4. La CoffeeShopsuperclase no tiene información sobre qué producto específico ( Coffee) se creará.
  5. La CoffeeShopsuperclase delega la creación de un producto específico a sus descendientes.
  6. Cada descendiente de la CoffeeShopclase implementa un createCoffee()método de fábrica de acuerdo con sus propias características específicas. En otras palabras, las implementaciones de las clases de productores preparan productos específicos basados ​​en las especificaciones de la clase de productores.
Ahora está listo para la definición del patrón del método de fábrica . El patrón del método de fábrica define una interfaz para crear un objeto, pero permite que las subclases seleccionen la clase del objeto creado. Por lo tanto, un método de fábrica delega la creación de una instancia a las subclases. En general, recordar la definición no es tan importante como entender cómo funciona todo.

Estructura de un método de fábrica

Patrones de diseño: método de fábrica - 3El diagrama anterior muestra la estructura general del patrón del método de fábrica. ¿Qué más es importante aquí?
  1. La clase Creator implementa todos los métodos que interactúan con los productos, excepto el método de fábrica.
  2. El método abstracto factoryMethod()debe ser implementado por todos los descendientes de la Creatorclase.
  3. La ConcreteCreatorclase implementa el factoryMethod()método, que crea directamente el producto.
  4. Esta clase es responsable de crear productos específicos. Esta es la única clase con información sobre la creación de estos productos.
  5. Todos los productos deben implementar una interfaz común, es decir, deben ser descendientes de una clase de producto común. Esto es necesario para que las clases que usan productos puedan operar sobre ellos como abstracciones, en lugar de implementaciones específicas.

Tarea

Hoy hemos trabajado bastante y hemos estudiado el patrón de diseño del método de fábrica. ¡Es hora de reforzar el material! Ejercicio 1. Haz el trabajo para abrir otra cafetería. Puede ser una cafetería estilo inglés o estilo español. O incluso al estilo de una nave espacial. ¡Agregue colorante para alimentos al café para que brille, y su café simplemente estará fuera de este mundo! Ejercicio 2. En la última lección , tuvo un ejercicio en el que creó una barra de sushi virtual o una pizzería virtual. Ahora tu ejercicio es no quedarte quieto. Hoy aprendiste cómo usar el patrón del método de fábrica a tu favor. Es hora de usar este conocimiento y expandir tu propio negocio ;)
Comentarios
  • Populares
  • Nuevas
  • Antiguas
Debes iniciar sesión para dejar un comentario
Esta página aún no tiene comentarios