JavaRush /Blogue Java /Random-PT /Método de fábrica e padrões abstratos de fábrica

Método de fábrica e padrões abstratos de fábrica

Publicado no grupo Random-PT
No livro “De cabeça. Design Patterns” define esses padrões da seguinte forma: O padrão Factory Method define a interface para criação de um objeto, mas permite que as subclasses escolham a classe da instância a ser criada. Assim, o método Factory delega a operação de instanciação às subclasses. O padrão Abstract Factory fornece uma interface para criar famílias de objetos inter-relacionados ou interdependentes sem especificar suas classes concretas. Vamos tentar entender isso com mais detalhes. Digamos que você decida escrever um jogo sobre pessoas que decidem se tornar... (você precisa de algo original e incomum aqui) monges. Poderíamos começar com o seguinte. 1) Crie uma classe Monk e classes filhas (vamos criar uma primeiro):
public abstract class Monk {

    public abstract void description();
}
public class OrthodoxMonk extends Monk {
    @Override
    public void description() {
        System.out.println("Я православный монах");
    }
}
2) E claro, crie uma classe Monastério, na qual você possa 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;
    }
}
Bem, vamos verificar o resultado:
public class Main {
    public static void main(String[] args) {
        Monastery monastery = new Monastery();
        monastery.createMonk("ORTODOX");
        monastery.getMonk().description();
    }
}
Я православный монах
Agora se você precisar criar... um monge católico, você precisará A) Criar uma nova classe para um monge católico:
public class CatholicMonk extends Monk {
    @Override
    public void description() {
        System.out.println("Я католический монах");
    }
}
B) Faça alterações na classe do mosteiro:
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;
    }
}
e assim, toda vez que novos tipos de monges forem introduzidos, você terá que criar uma nova classe e editar a existente. O que pode ser feito neste caso para de alguma forma “encapsular” nossa classe monástica das mudanças. Você pode tentar usar o padrão Factory Method. Como ficará A) Vamos deixar a classe dos monges como está, exceto talvez adicionar um monge anglicano (não apenas católicos e cristãos ortodoxos têm 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) Vamos alterar a classe do mosteiro da seguinte maneira (vamos torná-la e seu método abstratos). Aqui usamos apenas o método Factory:
public abstract class Monastery {
    protected abstract Monk createMonk();
}
e crie classes filhas com implementação 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) Vamos verificar o 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();
    }
}
Я православный монах
Я католический монах
Я англиканский монах
Aqueles. como vemos agora, ao adicionar novos tipos de monges, não haverá necessidade de alterar as classes existentes, mas apenas se necessário, adicionar novas (a classe de um mosteiro e monge específico). Talvez alguém já tenha notado que o método description, que estava desde o início na classe Monk, também era Factory :) A definição do método factory dizia que nosso padrão define a interface para criação de um objeto, mas não criamos nenhum interfaces, embora pudéssemos criar a classe Monastery como uma interface e implementá-la em implementações específicas. Isso se refere à palavra “interface” em um sentido mais amplo. A definição também dizia que permite que as subclasses escolham a classe da instância que criam . Aqui vemos apenas que as subclasses (classes filhas) implementam esse método (ou seja, esses poderes para criar objetos monge são delegados a elas). Agora vamos expandir um pouco o nosso programa, introduzindo a possibilidade de existirem diferentes monges em uma ou outra denominação. Por exemplo, na Ortodoxia, com base na posição da Igreja Ortodoxa sobre mosteiros e monásticos (adotada no Conselho de Bispos da Igreja Ortodoxa Russa de 29 de novembro a 2 de dezembro de 2017), podemos concluir que existem 2 tipos de monges : - Esquema menor (manto). - Esquema (ótimo esquema). Existem também “etapas preparatórias”, mas as pessoas não são consideradas monges (Trudnik, Noviço e Ryasophor ou Monge), porque não fazem votos monásticos. Portanto, não os levamos em consideração. O que obtemos neste caso: A) Aula de Mosteiro (para simplificar, vamos nos concentrar no monaquismo ortodoxo por enquanto) com o método Fábrica :
public abstract class Monastery {
    protected abstract Monk createMonk(String type);
}
e um mosteiro específico
public class OrthodoxMonastery extends Monastery {

    @Override
    protected Monk createMonk(String type) {
        return new OrthodoxMonk(type);
    }
}
B) Vamos consertar a classe monge:
public abstract class Monk {
    String kind;

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

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

    @Override
    public void description() {
        System.out.println("Я православный монах - " + kind);
    }
}
C) Vamos verificar nosso código:
public class Main {
    public static void main(String[] args) {
        Monastery monastery = new OrthodoxMonastery();
        monastery.createMonk("Мантийный монах").description();
        monastery.createMonk("Великосхимник").description();
    }
}
Я православный монах - Мантийный монах
Я православный монах — Великосхимник
Assim, ao utilizar o padrão Factory Method, conseguimos que não seja necessário alterar as classes escritas anteriormente, mas também ao expandir as imagens (tipos) dos monges, é necessário um mínimo de alterações no código. Vamos verificar e somar todas as ordens e congregações de monges católicos :) Mas é melhor focar nos 3 mais famosos, pois são mais de 100: 1) Beneditino 2) Jesuíta 3) Franciscano Para fazer isso, como antes com o monge ortodoxo, precisamos implementar uma classe específica de monge católico:
public class CatholicMonk extends Monk {
    public CatholicMonk(String kind) {
        super(kind);
    }

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

    @Override
    protected Monk createMonk(String type) {
        return new CatholicMonk(type);
    }
}
e verifique o 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();
    }
}
Я православный монах - Мантийный монах
Я православный монах - Великосхимник
Я католический монах - Бенедиктинец
Я католический монах - Иезуит
Я католический монах - Францисканец
Vamos terminar com esse padrão. Todos esses tipos de monges também poderiam ser adicionados à classe E-num antecipadamente, mas para simplificar o código faremos sem eles. Chegou a hora do padrão Abstract Factory. Temos monges, agora poderíamos fazer roupas, rosários, etc. Comecemos pelas roupas, ou seja, se voltarmos à nossa definição do início, as roupas se tornarão uma família de objetos interligados ou interdependentes . Vamos começar com o problema de que cada tipo de monge possui vestes diferentes. Se adicionarmos também os budistas, eles serão completamente diferentes :) Para isso, podemos criar uma interface de fábrica, cujas implementações criariam as roupas necessárias. Portanto A) Criamos uma fábrica para fazer roupas
public interface MonkFactory {
    Clothing createClothing();
}
e sua implementação
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();
    }
}
Bem, não vamos esquecer dos monges budistas :)
public class BuddhistMonkFactory implements MonkFactory {

    @Override
    public Clothing createClothing() {
        return new BuddhistClothing();
    }
}
B) Crie uma aula de vestuário (para simplificar, vamos pegar o elemento chave da roupa dos monges, não entraremos em detalhes):
public abstract class Clothing {
    private String name;

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

    public String getName() {
        return name;
    }
}
e aulas infantis
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 seguir, mudamos as classes dos monges para que tenham roupas:
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) A classe mosteiro contém nosso 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);
}
nossas implementações não mudaram
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 o 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();
    }
}
Я православный монах - Мантийный монах
Моя одежда - Мантия
Я католический монах - Иезуит
Моя одежда - Ряса с капюшоном
Я англиканский монах - Бенедиктинец
Моя одежда - Ряса
Я буддийский монах - Монах
Моя одежда - Кашая
A fábrica que cria roupas começou a funcionar bem. Agora você pode adicionar à fábrica a produção de equipamentos para monges para orações bem-sucedidas (rosários, etc.). Mas a questão ainda permanece: é possível usar 2 padrões juntos? Claro que pode :) Vamos tentar fazer a versão final do nosso projeto e adicionar um monge hindu: A) As fábricas agora criam monges que soam como uma “fábrica de estrelas” :
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) A classe Monastério + implementações concretas da classe Monastério não são necessárias, elas são implementadas pela fábrica (pelo contrário, poderíamos deixá-las e remover as fábricas, mas em essência elas seriam simplesmente em vez de fábricas, apenas em neste caso, o Monastério teria que se tornar uma interface, e não uma classe abstrata). E adicione a classe do aplicativo:
public class Application {

    public Monk create(MonkFactory monkFactory, String type) {
        Monk monk = monkFactory.createMonk(type);
        monk.prepare(monkFactory);
        return monk;
    }
}
B) Os monges agora contêm
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);
}
contém um método de fábrica em implementações, que é implementado usando uma 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) E vamos verificar:
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();
    }
}
Я православный монах - Мантийный монах
Моя одежда - Мантия
Я католический монах - Иезуит
Моя одежда - Ряса с капюшоном
Я англиканский монах - Бенедиктинец
Моя одежда - Ряса
Я буддийский монах - Монах
Моя одежда - Кашая
Я индуистский монах - Саньяси
Моя одежда - Почти ничего, тепло же :)
Concluindo, você pode notar que o método Factory utilizou uma classe abstrata com um método não implementado, que foi implementado em subclasses, e o Abstract Factory utilizou uma interface, onde a implementação (no nosso caso, criando um monge) ocorreu em classes que implementaram esta interface.
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION