Implementação e exemploImplementação e exemplo
6.6.9 Abstract Factory
Motivação, propósito e aplicabilidade
Motivação, propósito e aplicabilidade
Motivação, propósito e aplicabilidade
Motivação, propósito e aplicabilidade
Motivação, propósito e aplicabilidade
O padrão Abstract Factory (fábrica abstrata) se propõe a oferecer uma interface para a criação de famílias de objetos, de forma a isolar a aplicação das classes concretamente utilizadas para a instanciação de tais objetos, sendo neste sentido idêntico ao padrão Factory Method. A grande diferença é que uma fábrica abstrata pretende encapsular o mecanismo de seleção das famílias a serem utilizadas, acrescentando uma camada adicional de isolamento entre a aplicação e as classes utilizadas, sendo assim um mecanismo de criação abstrato mais sofisticado que o método fábrica.
467
Sistemas destinados a operarem em diferentes plataformas computacionais usualmente utilizam diferentes famílias de classes específicas dessas plataformas. Para que tais sistemas possuam a maior portabilidade possível, devemos evitar utilizar diretamente essas classes no código produzido, tornando-se necessário algum mecanismo transparente de seleção de uma família ou outra, conforme a situação. Aplicações que, de forma configurável, utilizam alternativamente diferentes toolkits para seu funcionamento, podem igualmente ser beneficiados dessa separação, permitindo sua melhor adaptação a diferentes ambientes.
Esse padrão é aplicável às situações onde devemos não apenas isolar a criação de objetos dos locais de sua utilização, mas também quando são desejáveis configurações diferentes (em termos de famílias de objetos) ou ainda quando simplesmente se deseja fornecer apenas as interfaces dos objetos que serão usados. Por outro lado, tende a ser mais complexo que o padrão Factory Method, pois exige uma hierarquia para as fábricas e não apenas para os produtos. Assim, a escolha entre esses padrões deve ser baseada na flexibilidade exigida.
Estrutura, participantes e conseqüências
Estrutura, participantes e conseqüências
Estrutura, participantes e conseqüências
Estrutura, participantes e conseqüências
Estrutura, participantes e conseqüências
A estrutura do padrão Abstract Factory é muito semelhante ao Factory Method, distinguindo-se pelo fato de existirem diversas implementações da fábrica, onde cada uma delas fornece produtos de uma mesma família. Os participantes identificados nesse padrão são:
±
Interface IAbstractFactoryOferece uma interface para obtenção de instâncias de fábricas (FactoryImpl), bem como especifica a interface para a obtenção dos produtos (ProductImpl).
±
Classe FactoryImplImplementação específica de uma fábrica que fornecerá diversos métodos capazes de fornecer uma família de produtos.
±
Interface IProductEspecifica uma interface genérica para cada tipo de produto, independentemente da família a que pertencem.
±
Classe ProductImplCorresponde às implementações específicas dos produtos de cada família.
Na Figura 6.14, temos um diagrama de classes que ilustra o relacionamento entre os participantes desse padrão.
468
Figura 6.14 Estrutura do padrão AbstractFactory.
A aplicação (ou cliente) que utilizará a fábrica abstrata deverá empregar a interface
IAbstractFactory para manipulá-la. Com relação aos objetos fornecidos, seu uso deverá ser realizado estritamente por meio das interfaces específicas de cada tipo.
Implementação e exemplo
Implementação e exemplo
Implementação e exemplo
Implementação e exemplo
Implementação e exemplo
Usos conhecidos e padrões relacionados
Usos conhecidos e padrões relacionados
Usos conhecidos e padrões relacionados
Usos conhecidos e padrões relacionados
Usos conhecidos e padrões relacionados
As fábricas abstratas são usualmente construídas com o uso de diversos métodos fábrica (padrão Factory Method), tanto para a obtenção da instância adequada da fábrica como das instâncias dos produtos. Seu uso é muito adequado nas situações onde famílias distintas de produtos similares são necessárias, tais como famílias de componentes visuais para diferentes plataformas
computacionais, daí a freqüente utilização desse padrão na estruturação de frameworks e toolkits. Usualmente as fábricas são implementadas como objetos Singleton (6.6.1), permitindo seu acesso global e evitando a possibilidade de duplicidade de fábricas e uso ineficiente de recursos do sistema.
6.6.10 Observer
Motivação, propósito e aplicabilidade
Motivação, propósito e aplicabilidade
Motivação, propósito e aplicabilidade
Motivação, propósito e aplicabilidade
Motivação, propósito e aplicabilidade
Nos casos onde existam um ou mais objetos interessados no estado de um outro objeto particular, esse padrão é adequado para descrever como estabelecer um relacionamento entre o objeto, cujo estado é observado, e qualquer número de objetos observadores, sem que estes tenham que perceber quando ocorre a mudança de estado do objeto observado.
469
O padrão Observer define uma relação de dependência de um para muitos (1:n) de modo que, quando um objeto tem seu estado modificado, seus observadores são notificados e atualizados automaticamente. Temos assim que o objeto observado torna-se responsável por avisar seus observadores da ocorrência de uma alteração, provocando sua atualização.
Isso o torna aplicável às situações onde:
±
O objeto observado não pode fazer considerações sobre quem são seus observadores nem seu número.±
É necessário separar os aspectos relacionados ao modelo de dados (contidos no objeto observado) e seu tratamento (objetos observadores).±
Desejamos variar independentemente tanto os observadores como os observados.Estrutura, participantes e conseqüências
Estrutura, participantes e conseqüências
Estrutura, participantes e conseqüências
Estrutura, participantes e conseqüências
Estrutura, participantes e conseqüências
São quatro os participantes do padrão Observer:
±
Interface IObservedDeclara uma interface que permite a adição e a remoção de objetos observadores (que implementam a interface IObserver).
Especifica operação de notificação dos observadores. Implicitamente requer uma lista de objetos observadores.
±
Classe ObservedImplArmazena informações necessárias para os observadores (por exemplo, por meio de instâncias de uma classe State).
Mantém uma lista com os observadores registrados.
Notifica os observadores registrados quanto às mudanças no estado armazenado.
±
Interface IObserverEspecifica uma interface que deve ser implementada por classes (ObserverImpl) cujos objetos deverão ser notificados da alteração do estado de outros (ObservedImpl).
±
Classe ObserverImplImplementação específica das classes observadoras.
Mantém uma referência para o objeto observado de forma que seu estado possa ser obtido quando ocorrer a notificação.
470
Figura 6.15 Estrutura do padrão Observer.
Conforme as anotações existentes no diagrama, podemos perceber que as implementações dos observadores (ObserverImpl) se registrarão na implementação do observado (ObservedImpl), mantendo uma referência para os mesmos. Tais objetos observados deverão manter uma lista de objetos observadores interessados nas mudanças de seu estado, notificando-os quando estas ocorrerem, acionando o método update(), especificado na interface IObserver, para que os mesmos sejam atualizados. A atualização, correspondente à obtenção do estado, bem como as demais operações que os observadores (ObserverImpl) executam sobre os objetos observados (ObservedImpl), utilizam apenas os métodos especificados na interface IObserved.
Implementação e exemplo
Implementação e exemplo
Implementação e exemplo
Implementação e exemplo
Implementação e exemplo
No Exemplo 6.35, temos uma aplicação Swing que demonstra como um botão (JButton)
representa participante ObservedImpl (o elemento observado) do padrão Observer, permitindo o registro e a remoção de tantos observadores quantos desejados por meio de dois outros botões auxiliares posicionados na parte inferior da janela. Os observadores são implementados como uma classe independente (ButtonObserver) que faz o papel de ObserverImpl e que implementa a interface apropriada, isto é, ActionListener. Os botões auxiliares também são elementos observados por objetos instanciados a partir de classes anônimas declaradas na ocasião do registro dos listeners necessários.
471
// Observadores.java import java.awt.*; import java.awt.event.*; import javax.swing.*;
public class Observadores extends JFrame { private JButton b1, b2, b3;
private JTextArea taResult; public Observadores() {
super("Observadores");
// instancia e organiza componentes JPanel p = new JPanel();
p.setBorder(BorderFactory.createEtchedBorder()); p.add(b1 = new JButton("Observado"));
getContentPane().add("North", p);
JScrollPane sp = new JScrollPane(taResult = new JTextArea()); taResult.setEditable(false);
getContentPane().add("Center", sp);
p = new JPanel(new GridLayout(1, 3, 5, 5)); p.setBorder(BorderFactory.createEtchedBorder()); p.add(new JLabel("Observador"));
p.add(b2 = new JButton("Adicionar")); p.add(b3 = new JButton("Remover")); getContentPane().add("South", p);
// registra listener anônimo (observador) p/ botão 2 b2.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
b1.addActionListener(new ButtonObserver(taResult)); taResult.append("Observador adicionado.\n"); }
});
// registra listener anônimo (observador) p/ botão 2 b3.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
ActionListener al[] = b1.getActionListeners(); if (al.length>0) { b1.removeActionListener(al[0]); taResult.append("Observador removido.\n"); } } }); // outros ajustes setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(320, 240); }
public static void main(String a[]) { new Observadores().show(); }
}
class ButtonObserver implements ActionListener { private static int control = 0;
private int number;
472
public ButtonObserver(JTextArea ta) { number = ++control;
taResult = ta; }
public void actionPerformed(ActionEvent e) {
taResult.append("Observador #" + number + " notificado.\n"); }
}
Exemplo 6.35 Classe Observadores.
Executando essa aplicação, poderemos notar que o botão Observado inicialmente não provoca nenhuma reação quando acionado, pois ainda não possui listener (observadores) registrados. Arbitrariamente podemos adicionar ou remover tantos listener (observadores) quanto desejados. A cada adição ou remoção uma mensagem indicativa é fornecida. Acionando-se o botão Observado, cada observador existente emitirá uma mensagem indicando sua notificação, permitindo que outras ações possam ser tomadas, demonstrando como o padrão Observer é usado no tratamento de eventos das aplicações AWT ou Swing. Na Figura 6.16, podemos ver a aplicação Observadores em duas situações diferentes.
Figura 6.16 Aplicação Observadores.
Usos conhecidos e padrões relacionados
Usos conhecidos e padrões relacionados
Usos conhecidos e padrões relacionados
Usos conhecidos e padrões relacionados
Usos conhecidos e padrões relacionados
Como vimos, esse padrão é utilizado pelo modelo de eventos do Java (proposto a partir da versão 1.1) para estabelecer o relacionamento entre os componentes e as rotinas de tratamento dos eventos lançados por esses componentes. Os componentes são as implementações concretas dos elementos observados (participantes ObservedImpl) enquanto as classes onde residem as rotinas de tratamento dos eventos são os observadores (participantes ObserverImpl). As interfaces que devem ser implementadas para o processamento dos eventos (por exemplo, ActionListener, TextListener, ItemListener etc.) são as interfaces dos observadores (IObserver), enquanto a raiz da
473
hierarquia dos componentes (Component ou JComponent) é o responsável por fornecer a interface para o registro e a remoção dos observadores, além do mecanismo de notificação (IObserved).
Uma das primeiras aplicações conhecidas desse padrão foi o modelo MVC (Model-View-Control) utilizado pelo Smalltalk. O Swing utiliza esse modelo, pois boa parte dos componentes que não são containers possuem um modelo independente para representação de dados ou estado, o controle é exercido pelo componente em si enquanto sua visualização pode variar livremente conforme o Look and Feel escolhido.