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:

Diagrama de classes

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:

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:

diagrama

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.