JavaRush /Blog Java /Random-ES /Método de fábrica y patrones abstractos de fábrica

Método de fábrica y patrones abstractos de fábrica

Publicado en el grupo Random-ES
En el libro “La cabeza primero. Patrones de diseño” define estos patrones de la siguiente manera: El patrón Método de fábrica define la interfaz para crear un objeto, pero permite a las subclases elegir la clase de la instancia que se creará. Por tanto, el método Factory delega la operación de creación de instancias a subclases. El patrón Abstract Factory proporciona una interfaz para crear familias de objetos interrelacionados o interdependientes sin especificar sus clases concretas. Intentemos entender esto con más detalle. Digamos que decides escribir un juego sobre personas que deciden convertirse... (aquí necesitas algo original e inusual) monjes. Podríamos empezar con lo siguiente. 1) Cree una clase Monk y clases secundarias (creemos una primero):
public abstract class Monk {

    public abstract void description();
}
public class OrthodoxMonk extends Monk {
    @Override
    public void description() {
        System.out.println("Я православный монах");
    }
}
2) Y por supuesto, crea una clase de Monasterio, en la que puedas implementar “votos monásticos”:
public class Monastery {
    private Monk monk;

    public void createMonk(String typeName) {
        this.monk = switch (typeName) {
            case "ORTODOX" -> new OrthodoxMonk();
            default -> null;
        };
    }

    public Monk getMonk() {
        return monk;
    }
}
Bueno, comprobemos el resultado:
public class Main {
    public static void main(String[] args) {
        Monastery monastery = new Monastery();
        monastery.createMonk("ORTODOX");
        monastery.getMonk().description();
    }
}
Я православный монах
Ahora, si necesita crear... un monje católico, necesitará A) Crear una nueva clase para un monje católico:
public class CatholicMonk extends Monk {
    @Override
    public void description() {
        System.out.println("Я католический монах");
    }
}
B) Realizar cambios en la clase del monasterio:
public class Monastery {
    private Monk monk;

    public void createMonk(String typeName) {
        this.monk = switch (typeName) {
            case "ORTODOX" -> new OrthodoxMonk();
            case "CATHOLIC" -> new CatholicMonk();
            default -> null;
        };
    }

    public Monk getMonk() {
        return monk;
    }
}
Por lo tanto, cada vez que se introduzcan nuevos tipos de monjes, tendrás que crear una nueva clase y editar la existente. ¿Qué se puede hacer en este caso para “proteger” de algún modo a nuestra clase de monasterio de los cambios? Puedes intentar usar el patrón Factory Method. Cómo se verá A) Dejemos la clase de monjes como está, excepto quizás agregando un monje anglicano (no solo los católicos y los cristianos ortodoxos tienen monaquismo):
public abstract class Monk {

    public abstract void description();
}
public class OrthodoxMonk extends Monk {
    @Override
    public void description() {
        System.out.println("Я православный монах");
    }
}
public class CatholicMonk extends Monk {
    @Override
    public void description() {
        System.out.println("Я католический монах");
    }
}
public class AnglicanMonk extends Monk {
    @Override
    public void description() {
        System.out.println("Я англиканский монах");
    }
}
B) Cambiemos la clase monasterio de la siguiente manera (hagámosla abstracta y su método). Aquí solo usamos el método Factory:
public abstract class Monastery {
    protected abstract Monk createMonk();
}
y crear clases secundarias con implementación de método:
public class OrthodoxMonastery extends Monastery {
    @Override
    protected Monk createMonk() {
        return new OrthodoxMonk();
    }
}
public class CatholicMonastery extends Monastery {
    @Override
    protected Monk createMonk() {
        return new CatholicMonk();
    }
}
public class AnglicanMonastery extends Monastery {
    @Override
    protected Monk createMonk() {
        return new AnglicanMonk();
    }
}
B) Comprobemos el código.
public class Main {
    public static void main(String[] args) {
        Monastery monastery;

        monastery = new OrthodoxMonastery();
        monastery.createMonk().description();

        monastery = new CatholicMonastery();
        monastery.createMonk().description();

        monastery = new AnglicanMonastery();
        monastery.createMonk().description();
    }
}
Я православный монах
Я католический монах
Я англиканский монах
Aquellos. Como vemos ahora, al agregar nuevos tipos de monjes, no habrá necesidad de cambiar las clases existentes, sino solo si es necesario, agregar nuevas (la clase de un monasterio y monje específico). Quizás alguien ya haya notado que el método de descripción, que estuvo desde el principio en la clase Monk, también era Factory :) La definición del método factory decía que nuestro patrón define la interfaz para crear un objeto, pero no creamos ninguno. interfaces, aunque podríamos crear la clase Monasterio como interfaz e implementarla en implementaciones específicas. Esto se refiere a la palabra "interfaz" en un sentido más amplio. La definición también dice que permite a las subclases elegir la clase de la instancia que crean . Aquí simplemente vemos que las subclases (clases hijas) implementan este método (es decir, estos poderes para crear objetos monje se les delegan). Ahora ampliemos un poco nuestro programa, introduzcamos la posibilidad de que haya diferentes monjes en una denominación u otra. Por ejemplo, en la ortodoxia, basándose en la posición de la Iglesia Ortodoxa sobre los monasterios y los monasterios (adoptada en el Concilio de Obispos de la Iglesia Ortodoxa Rusa del 29 de noviembre al 2 de diciembre de 2017), podemos concluir que existen 2 tipos de monjes. : - Esquema menor (manto). - Esquema (gran esquema). También hay “etapas preparatorias”, pero las personas no son consideradas monjes (Trudnik, Novicio y Ryasophor o Monje), porque no hacen votos monásticos. Por tanto, no los tenemos en cuenta. Qué obtenemos en este caso: A) Clase de Monasterio (para simplificar, centrémonos en el monaquismo ortodoxo por ahora) con el método Factory :
public abstract class Monastery {
    protected abstract Monk createMonk(String type);
}
y un monasterio específico
public class OrthodoxMonastery extends Monastery {

    @Override
    protected Monk createMonk(String type) {
        return new OrthodoxMonk(type);
    }
}
B) Arreglemos la clase de monje:
public abstract class Monk {
    String kind;

    public Monk(String kind) {
        this.kind = kind;
    }

    public abstract void description();
}
y clase infantil:
public class OrthodoxMonk extends Monk {
    public OrthodoxMonk(String kind) {
        super(kind);
    }

    @Override
    public void description() {
        System.out.println("Я православный монах - " + kind);
    }
}
C) Revisemos nuestro código:
public class Main {
    public static void main(String[] args) {
        Monastery monastery = new OrthodoxMonastery();
        monastery.createMonk("Мантийный монах").description();
        monastery.createMonk("Великосхимник").description();
    }
}
Я православный монах - Мантийный монах
Я православный монах — Великосхимник
Así, al utilizar el patrón Factory Method, logramos que no tengamos que cambiar clases previamente escritas, pero además al expandir las imágenes (tipos) de monjes, se requiere un mínimo de cambios en el código. Revisemos y agreguemos todas las órdenes y congregaciones de monjes católicos :) Pero es mejor centrarse en los 3 más famosos, porque hay más de 100: 1) benedictino 2) jesuita 3) franciscano Para hacer esto, como antes con el monje ortodoxo, necesitamos implementar una clase específica de monje católico:
public class CatholicMonk extends Monk {
    public CatholicMonk(String kind) {
        super(kind);
    }

    @Override
    public void description() {
        System.out.println("Я католический монах - " + kind);
    }
}
y clase de monasterio:
public class CatholicMonastery extends Monastery {

    @Override
    protected Monk createMonk(String type) {
        return new CatholicMonk(type);
    }
}
y revisa el código:
public class Main {
    public static void main(String[] args) {
        Monastery monastery;

        monastery = new OrthodoxMonastery();
        monastery.createMonk("Мантийный монах").description();
        monastery.createMonk("Великосхимник").description();

        monastery = new CatholicMonastery();
        monastery.createMonk("Бенедиктинец").description();
        monastery.createMonk("Иезуит").description();
        monastery.createMonk("Францисканец").description();
    }
}
Я православный монах - Мантийный монах
Я православный монах - Великосхимник
Я католический монах - Бенедиктинец
Я католический монах - Иезуит
Я католический монах - Францисканец
Terminemos con este patrón. Todos estos tipos de monjes también podrían agregarse a la clase E-num de antemano, pero para simplificar el código prescindiremos de ellos. Es hora del patrón Abstract Factory. Tenemos monjes, ahora les podríamos hacer ropa, rosarios, etc. Empecemos por la ropa, es decir, si volvemos a nuestra definición del principio, la ropa pasará a ser una familia de objetos interconectados o interdependientes . Empecemos por el problema de que cada tipo de monje tiene túnicas diferentes. Si también agregamos budistas, serán completamente diferentes :) Para hacer esto, podemos crear una interfaz de fábrica, cuyas implementaciones crearían la ropa necesaria. Por eso A) Creamos una fábrica para confeccionar ropa.
public interface MonkFactory {
    Clothing createClothing();
}
y su implementación
public class OrthodoxMonkFactory implements MonkFactory {

        @Override
    public Clothing createClothing() {
        return new OrtodoxClothing();
    }
}
public class CatholicMonkFactory implements MonkFactory {

    @Override
    public Clothing createClothing() {
        return new CatholicClothing();
    }
}
public class AnglicanMonkFactory implements MonkFactory {

    @Override
    public Clothing createClothing() {
        return new AnglicanClothing();
    }
}
Bueno, no nos olvidemos de los monjes budistas :)
public class BuddhistMonkFactory implements MonkFactory {

    @Override
    public Clothing createClothing() {
        return new BuddhistClothing();
    }
}
B) Crea una clase de ropa (para simplificar, tomemos el elemento clave de la ropa de los monjes, no entraremos en detalles):
public abstract class Clothing {
    private String name;

    public Clothing(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}
y clases infantiles
public class OrtodoxClothing extends Clothing {
    public OrtodoxClothing() {
        super("Мантия");
    }
}
public class CatholicClothing extends Clothing {
    public CatholicClothing() {
        super("Ряса с капюшоном");
    }
}
public class AnglicanClothing extends Clothing {
    public AnglicanClothing() {
        super("Ряса");
    }
}
public class BuddhistClothing extends Clothing {
    public BuddhistClothing() {
        super("Кашая");
    }
}
C) A continuación, cambiamos las clases de los monjes para que tengan ropa:
public abstract class Monk {
    String kind;
    Clothing clothing;

    public Monk(String kind) {
        this.kind = kind;
    }

    public void setClothing(Clothing clothing) {
        this.clothing = clothing;
    }

    public abstract void description();
}
public class OrthodoxMonk extends Monk {
    public OrthodoxMonk(String kind) {
        super(kind);
    }

    @Override
    public void description() {
        System.out.println("Я православный монах - " + kind);
        System.out.println("Моя одежда - " + clothing.getName());
    }
}
public class CatholicMonk extends Monk {
    public CatholicMonk(String kind) {
        super(kind);
    }

    @Override
    public void description() {
        System.out.println("Я католический монах - " + kind);
        System.out.println("Моя одежда - " + clothing.getName());
    }
}
public class AnglicanMonk extends Monk {
    public AnglicanMonk(String kind) {
        super(kind);
    }

    @Override
    public void description() {
        System.out.println("Я англиканский монах - " + kind);
        System.out.println("Моя одежда - " + clothing.getName());
    }
}
public class BuddhistMonk extends Monk {
    public BuddhistMonk(String kind) {
        super(kind);
    }

    @Override
    public void description() {
        System.out.println("Я буддийский монах - " + kind);
        System.out.println("Моя одежда - " + clothing.getName());
    }
}
D) La clase monasterio contiene nuestro método Factory
public abstract class Monastery {

    public Monk create(MonkFactory monkFactory, String type) {
        Monk monk = createMonk(type);
        monk.setClothing(monkFactory.createClothing());
        return monk;
    }

    protected abstract Monk createMonk(String type);
}
nuestras implementaciones no han cambiado
public class OrthodoxMonastery extends Monastery {

    @Override
    protected Monk createMonk(String type) {
        return new OrthodoxMonk(type);
    }
}
public class CatholicMonastery extends Monastery {

    @Override
    protected Monk createMonk(String type) {
        return new CatholicMonk(type);
    }
}
public class AnglicanMonastery extends Monastery {

    @Override
    protected Monk createMonk(String type) {
        return new AnglicanMonk(type);
    }
}
public class BuddhistMonastery extends Monastery {

    @Override
    protected Monk createMonk(String type) {
        return new BuddhistMonk(type);
    }
}
D) Verifique el resultado:
public class Main {
    public static void main(String[] args) {
        Monastery monastery;

        monastery = new OrthodoxMonastery();
        monastery.create(new OrthodoxMonkFactory(), "Мантийный монах").description();

        monastery = new CatholicMonastery();
        monastery.create(new CatholicMonkFactory(), "Иезуит").description();

        monastery = new AnglicanMonastery();
        monastery.create(new AnglicanMonkFactory(), "Бенедиктинец").description();

        monastery = new BuddhistMonastery();
        monastery.create(new BuddhistMonkFactory(), "Монах").description();
    }
}
Я православный монах - Мантийный монах
Моя одежда - Мантия
Я католический монах - Иезуит
Моя одежда - Ряса с капюшоном
Я англиканский монах - Бенедиктинец
Моя одежда - Ряса
Я буддийский монах - Монах
Моя одежда - Кашая
La fábrica que confecciona ropa ha empezado a funcionar bien. Ahora se puede añadir a la fábrica la producción de equipos para que los monjes puedan orar con éxito (rosarios, etc.). Pero la pregunta sigue siendo: ¿es posible utilizar 2 patrones juntos? Por supuesto que puedes :) Intentemos hacer la versión final de nuestro proyecto y agregar un monje hindú: A) Las fábricas ahora crean monjes suena como una "fábrica de estrellas" :
public interface MonkFactory {
    Monk createMonk(String type);
    Clothing createClothing();
}
public class OrthodoxMonkFactory implements MonkFactory {

    @Override
    public Monk createMonk(String type){
        return new OrthodoxMonk(type);
    }

    @Override
    public Clothing createClothing() {
        return new OrtodoxClothing();
    }
}
public class CatholicMonkFactory implements MonkFactory {

    @Override
    public Monk createMonk(String type){
        return new CatholicMonk(type);
    }

    @Override
    public Clothing createClothing() {
        return new CatholicClothing();
    }
}
public class AnglicanMonkFactory implements MonkFactory {

    @Override
    public Monk createMonk(String type){
        return new AnglicanMonk(type);
    }

    @Override
    public Clothing createClothing() {
        return new AnglicanClothing();
    }
}
public class BuddhistMonkFactory implements MonkFactory {

    @Override
    public Monk createMonk(String type){
        return new BuddhistMonk(type);
    }

    @Override
    public Clothing createClothing() {
        return new BuddhistClothing();
    }
}
public class HinduMonkFactory implements MonkFactory {

    @Override
    public Monk createMonk(String type){
        return new HinduMonk(type);
    }

    @Override
    public Clothing createClothing() {
        return new HinduClothing();
    }
}
B) La clase monasterio + implementaciones concretas de la clase Monasterio no son necesarias, son implementadas por la fábrica (por el contrario, podríamos dejarlas y eliminar las fábricas, pero en esencia simplemente serían en lugar de fábricas, solo en En este caso, el Monasterio tendría que convertirse en una interfaz, y no en una clase abstracta). Y agregue la clase de aplicación:
public class Application {

    public Monk create(MonkFactory monkFactory, String type) {
        Monk monk = monkFactory.createMonk(type);
        monk.prepare(monkFactory);
        return monk;
    }
}
B) Los monjes ahora contienen
public abstract class Monk {
    String kind;
    Clothing clothing;

    public Monk(String kind) {
        this.kind = kind;
    }

    public void setClothing(Clothing clothing) {
        this.clothing = clothing;
    }

    public abstract void description();

    public abstract void prepare(MonkFactory monkFactory);
}
contiene un método de fábrica en las implementaciones, que se implementa utilizando una fábrica:
public class OrthodoxMonk extends Monk {

    public OrthodoxMonk(String kind) {
        super(kind);
    }

    @Override
    public void description() {
        System.out.println("Я православный монах - " + kind);
        System.out.println("Моя одежда - " + clothing.getName());
    }

    @Override
    public void prepare(MonkFactory monkFactory) {
        setClothing(monkFactory.createClothing());
    }
}
public class CatholicMonk extends Monk {
    public CatholicMonk(String kind) {
        super(kind);
    }

    @Override
    public void description() {
        System.out.println("Я католический монах - " + kind);
        System.out.println("Моя одежда - " + clothing.getName());
    }

    @Override
    public void prepare(MonkFactory monkFactory) {
        setClothing(monkFactory.createClothing());
    }
}
public class AnglicanMonk extends Monk {
    public AnglicanMonk(String kind) {
        super(kind);
    }

    @Override
    public void description() {
        System.out.println("Я англиканский монах - " + kind);
        System.out.println("Моя одежда - " + clothing.getName());
    }

    @Override
    public void prepare(MonkFactory monkFactory) {
        setClothing(monkFactory.createClothing());
    }
}
public class BuddhistMonk extends Monk {
    public BuddhistMonk(String kind) {
        super(kind);
    }

    @Override
    public void description() {
        System.out.println("Я буддийский монах - " + kind);
        System.out.println("Моя одежда - " + clothing.getName());
    }

    @Override
    public void prepare(MonkFactory monkFactory) {
        setClothing(monkFactory.createClothing());
    }
}
public class HinduMonk extends Monk {
    public HinduMonk(String kind) {
        super(kind);
    }

    @Override
    public void description() {
        System.out.println("Я индуистский монах - " + kind);
        System.out.println("Моя одежда - " + clothing.getName());
    }

    @Override
    public void prepare(MonkFactory monkFactory) {
        setClothing(monkFactory.createClothing());
    }
}
D) Y comprobemos:
public class Main {
    public static void main(String[] args) {
        Application application = new Application();

        application.create(new OrthodoxMonkFactory(), "Мантийный монах").description();
        application.create(new CatholicMonkFactory(), "Иезуит").description();
        application.create(new AnglicanMonkFactory(), "Бенедиктинец").description();
        application.create(new BuddhistMonkFactory(), "Монах").description();
        application.create(new HinduMonkFactory(), "Саньяси").description();
    }
}
Я православный монах - Мантийный монах
Моя одежда - Мантия
Я католический монах - Иезуит
Моя одежда - Ряса с капюшоном
Я англиканский монах - Бенедиктинец
Моя одежда - Ряса
Я буддийский монах - Монах
Моя одежда - Кашая
Я индуистский монах - Саньяси
Моя одежда - Почти ничего, тепло же :)
En conclusión, se puede observar que el método Factory usó una clase abstracta con un método no implementado, que se implementó en subclases, y Abstract Factory usó una interfaz, donde la implementación (en nuestro caso, la creación de un monje) ocurrió en clases que implementaron esta interfaz.
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION