• Nenhum resultado encontrado

aula09 projetoSistemas ProjetoArquitetural Observer 2012.2

N/A
N/A
Protected

Academic year: 2021

Share "aula09 projetoSistemas ProjetoArquitetural Observer 2012.2"

Copied!
42
0
0

Texto

(1)

Estruturas de Controle e o

Padrão Observer

Projeto de Sistemas

(2)
(3)

Problema

• Como acoplar objetos entre si:

– De forma a que não se conheçam em tempo de

compilação

• Não queremos fazer

"referênciaAUmObjetoConhecido.método()"

– De forma a criar o acoplamento e desfazê-lo a

qualquer momento em tempo de execução

• Solucionar isso fornece uma implementação

(4)

Objetivo

• O padrão Observer permite que objetos

interessados sejam avisados da mudança de

estado ou outros eventos ocorrendo num

(5)

Objetos participantes

• O objeto sendo

observado é chamado de:

– "Subject" (GoF)

– "Observable" (java.util)

– "Source" ou "Event

Source" (java.swing e

java.beans)

– Provedor de informação

(Bill Venners)

– Gerador de eventos (Bill

Venners)

• O objeto que observa é

chamado de

– Observer (GoF e java.util)

– Listener (java.swing)

• Java usa este padrão em 2 lugares mas de formas diferentes!

• A forma java.util não é boa

• Usaremos as palavras

(6)

Também chamado de

• Publisher-Subscriber

• Event Generator

(7)

Exemplo

• Como projetar um sistema que modele um

telefone e todos os objetos que poderiam

estar interessados quando ele toca?

• Os objetos interessados poderiam ser:

– Pessoas que estejam perto (na mesma sala)

– Uma secretária eletrônica

– Um FAX

(8)

Exemplo (cont.)

• Os objetos interessados podem mudar

dinamicamente

– Pessoas entram e saem da sala onde o telefone está

– Secretárias eletrônicas, FAX, etc. podem ser adicionados

ou removidos durante a execução do programa

– Novos dispositivos poderão ser inventados e

adicionados em versões futuras do programa

• Qual é a solução básica de projeto?

(9)

A solução

• Em Java, um objeto (o Source) envia

informação para outro objeto (o Listener) pela

chamada de um método do Listener

• Mas, para que isso seja possível:

– O Source deve ter uma referência ao Listener

– O tipo desta referência deve ser uma classe ou

(10)
(11)

A solução idiomática em Java

• Etapa 1: Definir classes de categorias de eventos

• Etapa 2: Definir interfaces de Listener

• Etapa 3: Definir classes de adaptação (opcional)

• Etapa 4: Definir a classe observável Source

(12)

Exemplo de código em Java

• Arquivo TelefoneEvent.java (classe de categoria de

eventos)

• Arquivo TelefoneListener.java (interface de Listener)

• Arquivo TelefoneAdapter.java (classe Adapter)

• Arquivo Telefone.java (definição de source)

• Arquivo SecretariaEletronica.java (classe Listener)

• Arquivo Pessoa.java (classe Listener)

(13)

Etapa 1: Definir classes de categorias de eventos

• Defina uma classe separada de evento para cada

categoria principal de eventos que poderão ser

gerados pelo Source

– Nós teremos uma única categoria: "Telefone"

• Faça com que cada classe de eventos estenda

java.util.EventObject

• Faça com que cada classe encapsule a informação a ser

propagada para os Listeners

• Use nomes de classe que terminem em Event

(exemplo: TelefoneEvent)

(14)

Arquivo TelefoneEvent.java

• Por simplicidade, estamos encapsulando apenas os dados de

data/hora (currentTime) neste evento, mas seria possível incluir

mais:

public class TelefoneEvent extends java.util.EventObject {

private long currentTime;

public TelefoneEvent(Telefone source) { super(source);

currentTime = System.currentTimeMillis(); }

public long getCurrentTime() { return currentTime;

} }

(15)

Arquivo TelefoneEvent.java

• Observe que source é passado como parâmetro e

armazenado no objeto (super(source) faz isso)

– Isso permite que quem recebe o evento faça

java.util.EventObject.getSource() para saber qual objeto gerou

o evento

– Permite que um mesmo objeto seja Listener de vários objetos

Source

– Também permite que, com esta referência ao Source, o Listener

acione outros métodos do objeto para obter informação

• Chama-se este modelo de "Pull model"

• No "Push model", toda a informação necessária está presente dentro do evento

(16)

Etapa 2: Definir interfaces de Listener

• Para cada categoria de eventos, defina uma

interface que estenda java.util.EventListener e que

contenha um método para cada evento (da

categoria) que vai gatilhar a propagação de

informação do Source para os Listeners

• Chame a interface como chamou a classe de

eventos, com terminação Listener em vez de Event

(17)

Etapa 2: Definir interfaces de Listener

(cont.)

• Dê nomes aos métodos da interface para

descrever a situação que causou o evento. Use

um verbo no pretérito

– Exemplo: método telefoneTocou

• Cada método deve retornar void e aceitar um

parâmetro, uma referência a uma instância da

classe de eventos apropriada

(18)

Arquivo TelefoneListener.java

(interface de Listener)

public interface TelefoneListener extends java.util.EventListener {

void telefoneTocou(TelefoneEvent e); void telefoneAtendido(TelefoneEvent e); }

(19)

Etapa 3: Definir classes de adaptação

(opcional)

• Observação:

– É muito comum querer uma classe que implemente uma

interface fazendo nada para a maioria dos métodos e

fazendo algo útil apenas para alguns poucos métodos

– Para ajudar o programador, é comum, em Java, criar

uma classe "adapter" que implemente todos os métodos

de uma interface com métodos que nada fazem

• As classes que devem implementar a interface podem herdar

do adapter e fazer override de alguns poucos métodos

(20)

Etapa 3: Definir classes de adaptação

(cont.)

• Para cada interface de Listener que contenha

mais do que um método, defina uma classe

adapter que implemente a interface por

inteiro com métodos que nada fazem

• Dê um nome à classe com terminação Adapter

em vez de Listener

– Exemplo, para a interface TelefoneListener, a

classe seria TelefoneAdapter

(21)

Arquivo TelefoneAdapter.java

(classe Adapter)

public class TelefoneAdapter implements TelefoneListener {

void telefoneTocou(TelefoneEvent e) {}

void telefoneAtendido(TelefoneEvent e) {} }

(22)

Etapa 4: Definir a classe observável Source

• Para cada categoria de eventos que serão

propagados a partir de instâncias desta classe,

define um par de métodos públicos para

adicionar/remover Listeners

– Chame os métodos add<nome-da-interface-listener> e

remove<nome-da-interface-listener>

– Exemplo:

(23)

Etapa 4: Definir a classe observável Source

(cont.)

• Para cada método em cada interface de Listener,

define um método privado de propagação de eventos.

O método não aceita parâmetros e retorna void. Este

método propaga o evento para os Listeners

– Chame o método dispara<nome-do-método-listener>

• Exemplo: disparaTelefoneTocou()

• Chamadas ao método de disparo de eventos devem

ser adicionas em lugares apropriados da classe Source

– Nos lugares onde há mudança de estado ou ocorrência de

outros eventos interessantes

(24)

Uma solução simples, ignorando problemas

de concorrência: Telefone.java

import java.util.*;

public class Telefone {

private Collection<TelefoneListener> telefoneListeners = new ArrayList<TelefoneListener>();

// método de suporte para testar a solução public void tocaFone() {

disparaTelefoneTocou(); }

// método de suporte para testar a solução public void atendeFone() {

disparaTelefoneAtendido(); }

public void

(25)

Telefone.java (cont.)

...

public void removeTelefoneListener(TelefoneListener l) { telefoneListeners.remove(l);

}

private void disparaTelefoneTocou() {

TelefoneEvent evento = new TelefoneEvent(this); for (TelefoneListener t : telefoneListeners) {

t.telefoneTocou(evento); }

}

private void disparaTelefoneAtendido() {

TelefoneEvent evento = new TelefoneEvent(this); for (TelefoneListener t : telefoneListeners) {

t.telefoneAtendido(evento); }

(26)

Caso tenha problemas de concorrência,

use esta: Telefone2.java

import java.util.*;

public class Telefone2 {

private Collection<TelefoneListener> telefoneListeners = new ArrayList<TelefoneListener>();

// método de suporte para testar a solução public void tocaFone() {

disparaTelefoneTocou(); }

// método de suporte para testar a solução public void atendeFone() {

disparaTelefoneAtendido(); }

public synchronized void

(27)

Telefone2.java (cont.)

...

public synchronized void

removeTelefoneListener(TelefoneListener l) { telefoneListeners.remove(l);

}

private void disparaTelefoneTocou() { Collection <TelefoneListener> tl; synchronized (this) {

// Clonar para evitar problemas de sincronização // durante a propagação

tl = (Collection)(((ArrayList)

telefoneListeners).clone()); }

TelefoneEvent evento = new TelefoneEvent(this); for (TelefoneListener t : tl) {

(28)

Telefone2.java (cont.)

...

// disparaTelefoneAtendido() é semelhante a // disparaTelefoneTocou()

private void disparaTelefoneAtendido() { Collection <TelefoneListener> tl;

synchronized (this) {

tl = (Collection)(((ArrayList)

telefoneListeners).clone()); }

TelefoneEvent evento = new TelefoneEvent(this); for (TelefoneListener t : tl) {

t.telefoneAtendido(evento); }

(29)

Etapa 5: Definir objetos Listener

• Para ser um Listener de uma certa categoria

de eventos, basta implementar a interface de

Listener da categoria de eventos

– Isso é normalmente feito estendendo a classe

Adapter, mas não é obrigatório

(30)

No arquivo SecretariaEletronica.java:

public class SecretariaEletronica implements

TelefoneListener { public void telefoneTocou(TelefoneEvent e) {

System.out.println("Secretaria escuta o telefone tocando.");

}

public void telefoneAtendido(TelefoneEvent e) {

System.out.println("Secretaria sabe que o telefone foi atendido.");

} }

(31)

No arquivo Pessoa.java:

public class Pessoa {

public void escutaTelefone(Telefone t) { t.addTelefoneListener (

new TelefoneAdapter() {

public void telefoneTocou(TelefoneEvent e) { System.out.println("Eu pego!"); ((Telefone)(e.getSource())).atendeFone(); } } ); } }

• Por outro lado, o objeto Pessoa instancia uma "inner class"

anônima que estende TelefoneAdapter e faz override apenas

do método que interessa (telefoneTocou())

(32)

Finalmente, precisamos de uma aplicação

(arquivo ExemploFone.java)

public class ExemploFone {

public static void main(String[] args) { Telefone fone = new Telefone();

Pessoa fulano = new Pessoa();

SecretariaEletronica se= new SecretariaEletronica(); fone.addTelefoneListener(se);

fulano.escutaTelefone(fone);

fone.tocaFone(); // começa a brincadeira }

}

(33)

Quando usar o padrão Observer?

• Quando uma abstração tem dois aspectos, um

dependente do outro. Encapsular tais aspectos em objetos

separados permite que variem e sejam reusados

separadamente

• Quando uma mudança a um objeto requer mudanças a

outros e você não sabe quantos outros objetos devem

mudar

• Quando um objeto deve ser capaz de avisar outros sem

fazer suposições sobre quem são os objetos. Em outras

palavras, sem criar um acoplamento forte entre os objetos

(34)

Consequências do uso do padrão

• Permite que se variem objetos Source e Listeners

independentemente

– Pode-se reusar objetos Source sem reusar seus Listeners e

vice-versa

– Pode-se adicionar Listeners sem modificar o Source ou os

outros Listeners

• O acoplamento entre Source e Listeners é mínimo

– Basta que os Listeners implementem uma interface simples

– Os objetos envolvidos poderiam até pertencer a camadas

(35)

Consequências do uso do padrão (cont.)

• Suporte para comunicação em broadcast

– O Source faz broadcast do aviso. Os Listeners

podem fazer o que quiserem com o aviso,

incluindo ignorá-lo

• Do lado negativo: o custo de uma mudança ao

estado de um Source pode ser grande se

(36)
(37)

Estruturas de controle

• O paradigma de controle externo da aplicação

deve ser escolhido

• O paradigma diz como uma aplicação é

controlada "de fora" e como interage com

seus usuários e outras aplicações

• O controle interno dos objetos da aplicação

não é visível fora da aplicação

(38)

Controle orientado a procedimento

• Tem uma única thread no programa

• Métodos executam chamadas para obter dados de entrada e

esperam os dados

• O estado do programa pode ser guardado em variáveis locais

aos procedimentos (na pilha de execução)

• Vantagem: fácil de implementar com linguagens convencionais

• Desvantagem: qualquer paralelismo inerente aos objetos deve

ser mapeado a um fluxo de controle sequencial

(39)

Controle orientado a evento

• Um dispatcher é provido pela linguagem, SGBD

ou sistema operacional

• Métodos da aplicação são amarrados a eventos

• Quando o evento ocorre, o método

correspondente é chamado

• Todos os métodos retornam o controle para o

dispatcher em vez de esperar que os dados de

entrada cheguem

(40)

Controle orientado a evento (cont.)

• O estado do programa deve ser guardado em variáveis

globais já que os métodos retornam o controle (e não

ficam com ele)

• Vantagem: ajuda a separar a lógica da aplicação da

interface com o usuário

• Frequentemente usado com SGBD munido de linguagem

de quarta geração

• Muito usado com bancos de dados

(41)

Padrões de controle

• Dois padrões de controle frequentemente

utilizados na arquitetura são:

• Observer Pattern (ou Publish-Subscribe) para

acoplar objetos ou subsistemas

– Será descrito nesta aula

• Model-View-Controller (MVC) para separar os

objetos de domínio dos objetos de interface

(42)

Model-View-Controller (MVC)

• O Model é tudo que

envolve a Business Logic,

sem considerações de UI

• A View representa numa UI

o estado corrente do Model

• O Controller é usado para

alimentar o Model quando

ocorrem entradas na UI

Referências

Documentos relacionados

17 CORTE IDH. Caso Castañeda Gutman vs.. restrição ao lançamento de uma candidatura a cargo político pode demandar o enfrentamento de temas de ordem histórica, social e política

A vacina não deve ser administrada a imunodeprimidos, grávidas, menores de 1 ano de idade e hipersensibilidade a algum dos componentes da vacina e terapêutica concomitante

Mova a alavanca de acionamento para frente para elevação e depois para traz para descida do garfo certificando se o mesmo encontrasse normal.. Depois desta inspeção, se não

•   O  material  a  seguir  consiste  de  adaptações  e  extensões  dos  originais  gentilmente  cedidos  pelo 

Deste modo, o adequado zoneamento e sua observância são fundamentais para a conciliação da preservação ou conservação de espécies, hábitats e paisagens dentre outras e

também, de Augusto Abelaira. Organizado por Paulo Alexandre Pereira. Aveiro: Universidade de Aveiro, 2008. Grandes Romances).. Ensaio de

Devido ao reduzido número de estudos sobre fatores de risco para PI, optou-se por utilizar como categorias de referência as de menor risco para o BPN, conforme apontado pela

O KTAX-COMPLIANCE TRIBUTÁRIO executa análises digitais sobre os arquivos de dados do cliente (validação, cruzamentos, conciliações, relatórios gerenciais, indicadores e