Considere uma aplicação onde podemos ter múltiplas interfaces gráficas (a princípio apenas duas), devemos pensar em fazer isso da forma mais escalável possível, é aí que entra um padrão de Projeto: Abstract Factory.
Criaremos dois tipos de interfaces distintas: “KDE” e “Gnome”.
A ideia é que a aplicação não chame diretamente as classes concretas e que a adição ou remoção de um tipo de interface dê o menor trabalho possível, portanto vamos criar uma classe abstrata para que a classe cliente que necessitar de uma janela, por exemplo, não saiba se a janela é de “KDE” ou “Gnome”, ela tem de saber apenas que é uma janela.
Temos o seguinte diagrama:
As classes KDEFactory e GnomeFactory herdam de AbstractGui, ambas tem dependência da Classe AbstractJanela e AbstractBotao, porém não falaremos sobre isso agora.
A classe AbstractGui decidirá, em tempo de execução, qual a interface que deverá ser instanciada.
Temos então, os métodos sobrescritos nas duas classes-fábricas: CriarJanela() e CriarBotoes().
Caso em alguma dessas classes, ou até mesmo em outra que deva surgir, não haja a necessidade de mudança de comportamento padrão, a Classe AbstractGui possui um método padrão de criação de janelas e Botões.
A seguir, temos o seguinte diagrama:
Explicação: Cada Classe-Fábrica possui seus próprios botões e Janelas, herdando de AbstractBotao e AbstractJanela, pois assim, caso eu queira adicionar uma terceira interface com o nome “WindowMaker”, por exemplo, apenas criaria meu botão e minha janela nova, herdaria de AbstractBotao e AbstractJanela, respectivamente, e criaria um “WindowMakerFactory” herdando de AbstractGui.
O grande segredo está na classe AbstractGui, o Cliente chama apenas o método ObterInstancia() da classe referida, e esta, em tempo de execução, retorna, de acordo com algum contexto, uma instancia de KDE, Gnome ou outra futura interface implementada.
No método ObterInstancia(), teria algo equivalente a:
public static AbstractGuiObterInstancia()
{
switch(Constantes.Constantes.Tipo)
{
case"Gnome":
returnnew GnomeFactory();
case"KDE":
returnnew KDEFactory();
default:
return null;
}
}
Poderia ser uma Sessão, um Id de algum contexto, algum critério que diferenciasse o seu tipo de interface.
Segue o diagrama final:
Com isso, deixamos o sistema escalável, de fácil manutenção, e com um nível baixo de acoplamento, visto que a Classe Cliente não sabe o que é KDE, ou Gnome, apenas o usa.