Os padrões de projetos ajudam estruturar programas de maneiras mais flexíveis, fáceis de entender e manter. Através deles sistemas são construídos com boas qualidades de design orientado à objetos(OO). Eles nos fornecem soluções gerais para problemas de projeto. Esse artigo explica o padrão abstract factory mostrando seu uso e estrutura aplicada no contexto de uma loja de bolo.

No artigo sobre o Padrão factory method aprendemos uma maneira de encapsular a criação de objetos. Enquanto o padrão factory method utiliza a herança para criar uma instância, o padrão abstract factory utiliza a composição para criar uma família de produtos. Os dois padrões encapsulam a criação de objetos para manter os aplicativos levemente ligados e menos dependentes das implementações, ou seja, os clientes da fábrica são desvinculados dos produtos concretos reais que utilizam.

O diagrama de classes do padrão Abstract Factory é mostrado a seguir:

Um cliente é escrito para uma fábrica abstrata, no tempo de execução ele escolhe uma fábrica concreta a qual instância uma família de produtos (ProdutoA e ProdutoB, em nosso exemplo). A AbstractFactory define a interface que todas as fábricas concretas devem implementar. Dessa forma, a familía de produtos é estabelecida.

Aplicando o padrão Abstract Factory na loja de bolo:

O cliente de AbstractFactory é a instância de nossa CakeStore, DeliciousCake. A CakeIngredientFactory é a interface que define como criar uma família de produtos relacionados para fazer um bolo. A função das fábricas de bolo concretas é produzir os ingredientes dos bolos. Cada fábrica sabe como criar os objetos certos para um determinado tipo de bolo. A ChocolateCakeIngredientFactory cria massa convencional e cobertura de chocolate, enquanto a CoconutCakeIngredientFactory cria massa integral e cobertura de coco.

Código da classe CakeStore:

package com.programacao4devs.abstractfactory.cakestore;

import com.programacao4devs.abstractfactory.product.Cake;

public abstract class CakeStore {

	protected abstract Cake createCake(String type);
	
	public Cake orderCake(String type) {
		Cake cake;
		
		cake = createCake(type);
		
		cake.prepare();
		cake.bake();
		cake.cut();
		cake.box();
		
		return cake;
	}
}

Código da classe DeliciousCakes:

public class DeliciousCakes extends CakeStore {

	@Override
	protected Cake createCake(String type) {
		
		Cake cake = null;
		CakeIngredientFactory ingredientFactory;
		
		if (type.equals("chocolate")) {
			
			ingredientFactory = new ChocolateCakeIngredientFactory();
			cake = new ChocolateCake(ingredientFactory);
			cake.setName("Chocolate Cake");
			
		} else if (type.equals("coconut")) {
			
			ingredientFactory = new CoconutCakeIngredientFactory();
			cake = new CoconutCake(ingredientFactory);
			cake.setName("Coconut Cake");
		}
		
		return cake;
	}

}

Código da classe Cake:

public abstract class Cake {

	protected String name;
	protected Dough dough;
	protected Topping topping;
	
	public abstract void prepare();
	
	public void bake() {
		System.out.println("Bake for 40 minutes at 180");
	}
	
	public void cut() {
		System.out.println("Cutting the cake into square slices");
	}
	
	public void box() {
		System.out.println("Place cake in official CakeStore box");
	}
	
	public void setName(String name) {
		this.name = name;
	}
	
	public String getName() {
		return name;
	}

}

Código da classe CoconutCake:

public class CoconutCake extends Cake {

	CakeIngredientFactory ingredientFactory;
	
	public CoconutCake(CakeIngredientFactory ingredientFactory) {
		this.ingredientFactory = ingredientFactory;
	}
	
	@Override
	public void prepare() {
		System.out.println("Preparing " + name);
		dough = ingredientFactory.createDough();
		topping = ingredientFactory.createTopping();		
	}

}

Código da classe CoconutCakeIngredientFactory:


public class CoconutCakeIngredientFactory implements CakeIngredientFactory {

	@Override
	public Dough createDough() {
		return new WholeWheatDough();
	}

	@Override
	public Topping createTopping() {
		return new CoconutTopping();
	}

}

Código da classe ApplicationCakeStore para executar o teste:

public class ApplicationCakeStore {

	public static void main(String[] args) {
		CakeStore cakeStore = new DeliciousCakes();
		
		Cake cake = cakeStore.orderCake("coconut");
		System.out.println("Order placed for a " + cake.getName() );

	}

}

Resultado mostrado no Console:

	
Preparing Coconut Cake
Bake for 40 minutes at 180
Cutting the cake into square slices
Place cake in official CakeStore box
Order placed for a Coconut Cake
	

Download do código completo em: https://github.com/programacao4devs/padrao-abstract-factory

Os princípios OO utilizados no padrão abstract factory:

  • Encapsule o que varia;
  • Dê prioridade à composição em relação à herança;
  • Programa para interface, não para implementação;
  • Utilize baixo acoplamento (objetos levemente ligados);
  • As classes devem estar abertas à extensão, mas fechadas à modificação;
  • Dependa de abstrações. Não dependa de classes concretas.

Inscreva-se!

Inscreva seu nome e email na Programacao4Devs para receber atualizações de novos artigos, tutoriais e dicas.

Paulo Henrique de Morais

Mestre em Ciências da Computação pela Universidade Federal de Santa Catarina e atua no mercado de programação desde 2015.