JavaRush /Blog Java /Random-ES /Patrones de diseño: método de fábrica

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

Publicado en el grupo Random-ES
¡Hola! Hoy continuaremos estudiando patrones de diseño y hablaremos sobre el método de fábrica (FactoryMethod). Patrones de diseño: FactoryMethod - 1Descubrirá qué es y para qué tareas es adecuada esta plantilla. Examinaremos este patrón de diseño en la práctica y exploraremos su estructura. Para que todo lo anterior le quede claro, debe comprender los siguientes temas:
  1. Herencia en Java.
  2. Métodos y clases abstractos en Java.

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

En todos los patrones de diseño de fábricas, hay dos grupos de participantes: creadores (las propias fábricas) y productos (los objetos creados por las fábricas). Imagínese la situación: tenemos una fábrica que produce automóviles con la marca AutoRush. Sabe crear modelos de coches con diferentes tipos de carrocería:
  • sedanes
  • camionetas
  • cupé
Las cosas nos iban tan bien que un buen día absorbimos la empresa OneAuto. Como gerentes sensatos, no queremos perder clientes de OneAuto y nuestra tarea es reestructurar la producción de tal manera que podamos producir:
  • sedanes AutoRush
  • Furgonetas AutoRush
  • cupé AutoRush
  • Sedanes OneAuto
  • Camionetas OneAuto
  • cupé OneAuto
Como puede ver, en lugar de un grupo de productos derivados, aparecieron dos, que se diferencian en algunos detalles. El patrón de diseño del método de fábrica resuelve el problema de crear diferentes grupos de productos, cada uno con cierta especificidad. Consideraremos el principio de esta plantilla en la práctica, pasando gradualmente de lo simple a lo complejo, usando el ejemplo de nuestra cafetería, que creamos en una de las conferencias anteriores .

Un poco sobre la plantilla de fábrica.

Déjame recordarte: construimos una pequeña cafetería virtual contigo. En él aprendimos a crear diferentes tipos de café utilizando una sencilla fábrica. Hoy perfeccionaremos este ejemplo. Recordemos cómo era nuestra cafetería con una sencilla fábrica. Tuvimos una clase de café:
public class Coffee {
    public void grindCoffee(){
        // перемалываем кофе
    }
    public void makeCoffee(){
        // делаем кофе
    }
    public void pourIntoCup(){
        // наливаем в чашку
    }
}
Y también varios de sus herederos - tipos específicos de café que nuestra fábrica podía producir:
public class Americano extends Coffee {}
public class Cappuccino extends Coffee {}
public class CaffeLatte extends Coffee {}
public class Espresso extends Coffee {}
Para facilitar la aceptación de pedidos, hemos introducido transferencias:
public enum CoffeeType {
    ESPRESSO,
    AMERICANO,
    CAFFE_LATTE,
    CAPPUCCINO
}
La fábrica de café en sí tenía este aspecto:
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 por último, la cafetería en sí:
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("Вот ваш кофе! Спасибо, приходите еще!");
        return coffee;
    }
}

Modernización de una fábrica sencilla.

Nuestra cafetería está funcionando bien. Tanto es así que estamos pensando en ampliar. Queremos abrir varios puntos nuevos. Como emprendedores, no crearemos cafeterías monótonas. Quiero que cada uno tenga su propio toque. Por eso, para empezar, abriremos dos puntos: en estilo italiano y americano. Los cambios afectarán no sólo al interior, sino también a las bebidas:
  • En una cafetería italiana utilizaremos café exclusivamente de marcas italianas, con molienda y tostado especiales.
  • La porción americana será un poco más grande y con cada pedido serviremos malvaviscos derretidos: malvaviscos.
Lo único que no cambiará es nuestro modelo de negocio, que ya ha demostrado su eficacia. Si hablamos en lenguaje codificado, esto es lo que sucede. Teníamos 4 clases de productos:
public class Americano extends Coffee {}
public class Cappuccino extends Coffee {}
public class CaffeLatte extends Coffee {}
public class Espresso extends Coffee {}
Y se convierte en 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 {}
Como queremos mantener el modelo de negocio actual sin cambios, queremos que el método orderCoffee(CoffeeType type)experimente una cantidad mínima de cambios. Echemos un vistazo:
public Coffee orderCoffee(CoffeeType type) {
    Coffee coffee = coffeeFactory.createCoffee(type);
    coffee.grindCoffee();
    coffee.makeCoffee();
    coffee.pourIntoCup();

    System.out.println("Вот ваш кофе! Спасибо, приходите еще!");
    return coffee;
}
¿Qué opciones tenemos? Ya sabemos escribir fábrica, ¿verdad? Lo más simple que me viene a la mente de inmediato es escribir dos fábricas similares y luego pasar la implementación requerida a nuestra cafetería en el constructor. Entonces la clase de la cafetería no cambiará. Primero, necesitamos crear una nueva clase de fábrica, heredar de nuestra fábrica simple y anular el archivo createCoffee (CoffeeType type). Escribamos fábricas para crear café en estilos italiano y 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 requerida a CoffeeShop. Veamos cómo sería el código para pedir café en diferentes cafeterías. Por ejemplo, capuchino en estilos italiano y americano:
public class Main {
    public static void main(String[] args) {
        /*
            Закажем капучино в итальянском стиле:
            1. Создадим фабрику для приготовления итальянского кофе
            2. Создадим новую кофейню, передав ей в конструкторе фабрику итальянского кофе
            3. Закажем наш кофе
         */
        SimpleItalianCoffeeFactory italianCoffeeFactory = new SimpleItalianCoffeeFactory();
        CoffeeShop italianCoffeeShop = new CoffeeShop(italianCoffeeFactory);
        italianCoffeeShop.orderCoffee(CoffeeType.CAPPUCCINO);


         /*
            Закажем капучино в американском стиле
            1. Создадим фабрику для приготовления американского кофе
            2. Создадим новую кофейню, передав ей в конструкторе фабрику американского кофе
            3. Закажем наш кофе
         */
        SimpleAmericanCoffeeFactory americanCoffeeFactory = new SimpleAmericanCoffeeFactory();
        CoffeeShop americanCoffeeShop = new CoffeeShop(americanCoffeeFactory);
        americanCoffeeShop.orderCoffee(CoffeeType.CAPPUCCINO);
    }
}
Creamos dos cafeterías diferentes, trasladando cada una a la fábrica requerida. Por un lado, hemos logrado nuestro objetivo, pero por otro... Algo rasca el alma incontenible del emprendedor... Averigüemos qué pasa. En primer lugar, la abundancia de fábricas. ¿Es posible crear cada vez tu propia fábrica para un nuevo punto y, además, asegurarte de que al crear una cafetería, la fábrica requerida se transfiera al constructor? En segundo lugar, sigue siendo una fábrica sencilla. Sólo un poco modernizado. Todavía estamos estudiando un nuevo patrón aquí. En tercer lugar, ¿no es posible hacerlo de otra manera? Sería genial si pudiéramos localizar todas las preguntas sobre cómo preparar café dentro del aula CoffeeShop, vinculando los procesos de creación de café y servicio del pedido, pero al mismo tiempo manteniendo suficiente flexibilidad para preparar café en diferentes estilos. La respuesta es sí, puedes. Esto se denomina 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 el problema de la manera más eficiente posible, nosotros:
  1. Devolvamos el método createCoffee(CoffeeType type)a la clase CoffeeShop.
  2. Hagamos este método abstracto.
  3. La clase en sí se CoffeeShopvolverá abstracta.
  4. La clase CoffeeShoptendrá herederos.
Si amigo. Una cafetería italiana no es más que una heredera de la clase CoffeeShop, implementando un método createCoffee(CoffeeType type)acorde con las mejores tradiciones de los baristas italianos. Entonces, en orden. Paso 1. Hagamos la clase Coffeeabstracta. Ahora tenemos dos familias de productos diferentes. Las bebidas de café italianas y estadounidenses todavía comparten un ancestro común: el Coffee. Sería correcto hacerlo abstracto:
public abstract class Coffee {
    public void makeCoffee(){
        // делаем кофе
    }
    public void pourIntoCup(){
        // наливаем в чашку
    }
}
Paso 2. Hazlo CoffeeShopabstracto, con un método abstracto.createCoffee(CoffeeType type)
public abstract class CoffeeShop {

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

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

        System.out.println("Вот ваш кофе! Спасибо, приходите еще!");
        return coffee;
    }

    protected abstract Coffee createCoffee(CoffeeType type);
}
Paso 3. Crea una cafetería italiana, una clase descendiente de la cafetería abstracta. En él implementamos el método createCoffee(CoffeeType type)teniendo en cuenta las particularidades 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. Hagamos lo mismo con una cafetería de 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. Echemos un vistazo a cómo sería pedir un café con leche al estilo americano e italiano:
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 en nuestra cafetería.

Cómo funciona el método de fábrica

Ahora echemos un vistazo más de cerca a lo que tenemos. El siguiente diagrama muestra las clases resultantes. Los bloques verdes son clases de creadores, los bloques azules son clases de productos. Patrones de diseño: FactoryMethod - 2¿Qué conclusiones se pueden sacar?
  1. Todos los productos son implementaciones de la clase abstracta Coffee.
  2. Todos los creadores son implementaciones de la clase abstracta CoffeeShop.
  3. Observamos dos jerarquías de clases paralelas:
    • Jerarquía de productos. Vemos descendientes italianos y descendientes americanos.
    • Jerarquía de creadores. Vemos descendientes italianos y descendientes americanos.
  4. La superclase CoffeeShopno tiene información sobre qué implementación de producto específica ( Coffee) se creará.
  5. Una superclase CoffeeShopdelega la creación de un producto específico a sus descendientes.
  6. Cada clase descendiente CoffeeShopimplementa un método de fábrica createCoffee()de acuerdo con sus características específicas. En otras palabras, dentro de las implementaciones de las clases de creador, se toma la decisión de preparar un producto específico en función de las características específicas de la clase de creador.
Ahora está listo para definir el patrón del método de fábrica . El patrón del método de fábrica define la interfaz para crear un objeto, pero permite que las subclases elijan la clase de la instancia a crear. Por tanto, el método Factory delega la operación de creación de instancias a subclases. En general, recordar la definición no es tan importante como entender cómo funcionan las cosas.

Estructura del método de fábrica

Patrones de diseño: FactoryMethod - 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 contiene implementaciones de todos los métodos que interactúan con los productos, excepto el método de fábrica.
  2. factoryMethod()Todos los descendientes de la clase deben implementar un método abstracto Creator.
  3. La clase ConcreteCreatorimplementa un método factoryMethod()que produce directamente un 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: ser descendientes de una clase de producto común. Esto es necesario para que las clases que utilizan productos puedan operar en ellos a nivel de abstracciones en lugar de implementaciones concretas.

Tarea

Entonces, hoy trabajamos bastante y estudiamos el patrón de diseño del método de fábrica. ¡Es hora de consolidar el material que has cubierto! Tarea 1. Trabajar para abrir otra cafetería. Se puede hacer en estilo inglés o español. O incluso al estilo de una nave espacial. Agreguemos colorante alimentario al café para que brille y, en general, ¡el café será solo espacio! Tarea 2. En la última conferencia, tuviste la tarea de crear una barra de sushi virtual o una pizzería virtual. Tu tarea no es quedarte quieto. Hoy aprendiste cómo puedes utilizar el patrón del método de fábrica para lograr el éxito. Es hora de aprovechar este conocimiento y expandir tu propio negocio ;)
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION