1. Definição do modo de observador:
O modo Observador também é chamado de modo de publicação de assinatura, no qual um objeto de destino gerencia todos os objetos de observador dependentes e emite ativamente notificações quando seu estado muda. Isso geralmente é alcançado chamando os métodos de cada observador. Esse padrão é geralmente usado em sistemas de processamento de eventos.
1. Estrutura geral do padrão de observador
Primeiro, vamos dar uma olhada na descrição do diagrama de aula do padrão de observador:
Os papéis do modo Observador são os seguintes:
Assunto (interface do tópico abstrato): define uma série de operações na lista de observadores na classe temática, incluindo adição, exclusão, notificação etc.
Assunto concreto (classe de tópico específico):
Observador (interface do Obstract Observer): define o observador para aceitar operações no status de atualização da classe Topic.
ConcreteObServer (classe de observador específica): implementa a lógica, como as notificações da classe de tópico de atualização pela interface do observador.
A partir deste diagrama de classe, podemos ver que a classe temática mantém uma lista de classe que implementa a interface do observador. A classe temática usa esta lista para executar uma série de adições, deleções e modificações no observador. A classe Observer também pode chamar ativamente o método de atualização para entender as informações de atualização de status da classe temática.
Os diagramas de classe acima descrevem apenas a idéia básica do padrão do observador, e há muitas deficiências. Por exemplo, como observador, você também pode assinar ativamente determinados tópicos, etc. Os exemplos a seguir farão algumas alterações para aplicar a lógica de negócios específica.
2. Exemplo de modo de observador
Construímos uma aula de observador e tópico, onde os observadores podem se inscrever ativamente ou cancelar tópicos. A categoria tema é gerenciada uniformemente por um gerente de tema. A seguir, é apresentado um diagrama de classes:
Assunto:
Assunto da interface pública {// Registre um registro de vazio público do observador (observador observador); // Remova um vazio público do observador Remova (observador observador); // notifique todos os observadores public void notifyObServers (); // Obtenha a mensagem a ser publicada pela classe de tópico public string getMessage ();} concertesubject: public classe mySubject implementa assuntos {private list <sterver> observadores; O booleano privado mudou; mensagem de string privada; // Bloqueio de objeto, usado para atualizar de síncrono a lista de observador private final objeto mutex = new Object (); public mysubject () {observadores = new ArrayList <Tenerver> (); alterado = false; } @Override public void Register (observador observador) {if (observador == null) lança new nullPointerException (); // certifica não repetir se (! Observers.contains (observador)) observers.add (observador); } @Override public void Remover (Observer Observer) {Observers.remove (Observer); } @Override public void notifyObServers () {// Lista de temperaturas Lista <Beserver> tempobServers = null; sincronizado (mutex) {if (! alterado) retornar; tempobservers = new ArrayList <> (this.observers); this.changed = false; } para (observador obj: tempobservers) {obj.update (); }} // A classe de tema publica uma nova mensagem pública void makechanged (string message) {System.out.println ("O sujeito faz uma alteração:" + mensagem); this.Message = message; this.changed = true; notifyObServers (); } @Override public String getMessage () {return this.Message; }}Quando o concertesubject faz uma atualização, todos os observadores da lista são notificados e o método de atualização do observador é chamado para implementar a lógica após o recebimento da notificação. Observe o bloco de sincronização no NotifyObServers aqui. No caso de multi-threading, para evitar adicionar e excluir a lista de observadores por outros threads ao publicar notificações na classe de tópicos, uma lista temporária é usada no bloco de sincronização para obter a lista de observador atual.
Subvenço: gerente de classe temática
classe pública Subvenço -gestão {// Um nome de registro - mapa mapa privado <string, sujeito> sujeitoList = new Hashmap <string, sujeito> (); public void addSubject (nome da string, sujeito) {subjectList.put (nome, sujeito); } public void addSubject (sujeito) {sujeitoList.put (sujeito.getClass (). getName (), sujeito); } public substetBject (string subjectName) {return subjectList.get (sujeitoname); } public void removesubject (nome da string, sujeito) {} public void removesubject (sujeito) {} // Singleton Private Subvençando () {} public static Subvenço GetInstance () {Return subjectmaningInstance.inStance; } classe estática privada Subjetivo -gestãoInstance {estática Final Subvention Instância = new Subvençando (); }}O objetivo do gerente de tópicos é obter um objeto de instância do tópico quando o observador assinar um tópico.
Observador:
Public Interface Observer {public void update (); public void Setsubject (Assunto);} ConcerteObServer: public class Myobserver implementa observador {sujeito privado Assunto; // Obtenha a mensagem Notify do Concentro sujeito @Override public void update () {string message = sujeito.getMessage (); System.out.println ("de sujeito" + sujeito.getClass (). GetName () + "mensagem:" + mensagem); } @Override public void Setsubject (sujeito) {this.subject = sujeito; } // Subcirbe Algum sujeito public void assinando (string subjectName) {subjectManagement.getInstance (). getSubject (sujeitoname) .register (this); } // Cancelar inscrever public void cancelsubscribe (string substetName) {subjectManagement.getInstance (). getSubject (sujeitoname) .remove (this); }} Teste: abstraímos as aulas e observadores em escritores e leitores
classe pública Observertest {Private Static MySubject Writer; @BeFeforeClass public static void setupBeFeClass () lança exceção {writer = new mySubject (); // Adicione um escritor chamado Linus Subvenço -Gerenciamento.getInstance (). AddSubject ("Linus", escritor); } @Test public void test () {// Defina vários leitores myobserver leitor1 = new Myobserver (); Myobserver Reader2 = new Myobserver (); Myobserver Reader3 = new Myobserver (); Reader1.SetSubject (Writer); leitor2.SetSubject (escritor); Reader3.SetSubject (Writer); leitor1.subscribe ("linus"); leitor2.subscribe ("linus"); leitor3.subscribe ("linus"); writer.makechanged ("Eu tenho uma nova mudança"); leitor1.update (); }}O acima é um pequeno exemplo do padrão de observador. Pode -se observar que cada classe de tópico deve manter uma lista de observadores correspondente. Aqui, podemos abstrair ainda mais com base no nível abstrato do tópico específico, colocar essa reunião em uma classe abstrata para manter em conjunto uma lista. Obviamente, a operação específica depende da lógica de negócios real.
2. ouvinte no servlet
Vamos falar sobre o ouvinte no Servlet, vamos falar sobre outra forma do padrão de observador - o modelo orientado a eventos. Como o papel temático do padrão do observador mencionado acima, o modelo orientado a eventos inclui fontes de eventos, eventos específicos, ouvintes e ouvintes específicos.
O ouvinte no Servlet é um modelo típico orientado a eventos.
Há um conjunto de classes orientadas por eventos no JDK, incluindo uma interface do ouvinte unificado e uma fonte de evento unificado. O código -fonte é o seguinte:
/*** Uma interface de marcação que todas as interfaces do ouvinte de eventos devem se estender. * @since jdk1.1 */interface pública EventListener {}Esta é uma interface de sinalizador, e o JDK estipula que todos os ouvintes devem herdar essa interface.
classe pública EventObject implementa java.io.Serializable {private estático final serialversionuid = 5516075349620653480L; /*** O objeto no qual o evento ocorreu inicialmente. */ fonte de objeto transitório protegido; /*** Construa um evento de protótipo. * * @param fonte O objeto no qual o evento ocorreu inicialmente. * @Exception ilegalArgumentException Se a fonte for nula. */ public EventObject (Object Source) {if (fonte == null) lançar novo ilegalArgumentException ("fonte nula"); this.source = fonte; } /*** O objeto no qual o evento ocorreu inicialmente. * * @Return o objeto no qual o evento ocorreu inicialmente. */ public Object getSource () {return fonte; } /*** Retorna uma representação de string deste EventObject. * * @return a uma representação de string deste EventObject. */ public string tostring () {return getClass (). getName () + "[fonte =" + fonte + "]"; }}EvenObject é uma fonte de evento unificado especificado pelo JDK. A classe EvenObject define uma fonte de evento e um método GET para obter a fonte do evento.
Vamos analisar o processo de operação do ouvinte do servlet.
1. A composição do ouvinte do servlet
Atualmente, existem 6 tipos de interfaces do ouvinte para dois tipos de eventos em servlets, como mostrado na figura abaixo:
A situação específica do gatilho é a seguinte:
2. Um processo de acionamento específico do ouvinte
Vamos tomar o servletRequestattributelistener como um exemplo para analisar o processo orientado a eventos aqui.
Primeiro de tudo, quando o httpServletRequest chama o método setAtrilbute, ele é chamado de org.apache.catalina.connector.request#setattrilbute. Vamos dar uma olhada em seu código -fonte:
public void setAttribute (nome da string, valor do objeto) {... // O código lógico acima foi omitido // aqui o ouvinte é notificado de que o NotifyAttributESSIGN (nome, valor, antigo); }A seguir, é apresentado o código -fonte do notifyAttributeAssign (Nome da String, Valor do Objeto, Objeto OldValue)
private void notifyAttributeAssign (Nome da String, Valor do Objeto, Objeto OldValue) {// Obtenha o objeto de instância do ouvinte definido no WebApp dos ouvintes do objeto de contêiner [] = context.getApplicationEventListenners (); if ((ouvintes == null) || (ouvintes.length == 0)) {return; } booleano substituído = (antigo! = nulo); // Crie o evento de evento relacionado servletRequestattributerEvent Event = null; if (substituído) {Event = new ServletRequestAttributeevent (context.getServletContext (), getRequest (), nome, OldValue); } else {Event = new ServLetRequestAttributEEvent (context.getServletContext (), getRequest (), nome, valor); } // Tranquilidade através de todas as listas de ouvintes e encontre o ouvinte para o evento correspondente para (int i = 0; i <ouvintes.length; i ++) {if (! (Ouvintes [i] instanceof servletRequestAttributelistener)) {continuação; } // chamando o método do ouvinte para implementar a operação de escuta servletRequestattributElistener ouvinte = (servletRequestattributEListener) ouvintes [i]; tente {if (substituído) {listener.attributerEdRePlaced (Event); } else {listener.attributeAdded (evento); }} catch (throwable t) {excepcionutils.Handlethrowable (t); context.getLogger (). Error (sm.getString ("coyoteRequest.attributeEvent"), t); // A válvula de erro escolherá esta exceção e exibirá -a nos atributos do usuário.put (requestDispatcher.error_exception, t); }}}O exemplo acima mostra claramente como o servletRequestAttributElistener é chamado. Os usuários precisam apenas implementar a interface do ouvinte. Os ouvintes em servlets quase cobrem eventos em que você está interessado durante todo o ciclo de vida do servlet. O uso flexível desses ouvintes pode tornar o programa mais flexível.
3. Exemplos abrangentes
Por exemplo, se você assistiu aos filmes policiais e gangster da TVB, saberá como o trabalho disfarçado. Geralmente, um policial pode ter vários agentes disfarçados que se infiltram no inimigo e perguntam sobre informações. O agente disfarçado trabalha inteiramente sob as instruções de seu líder. O líder diz que ação ele deve seguir. Se o tempo de ação mudar, ele deve mudar imediatamente o tempo que coopera com a ação. Se o líder enviar dois agentes disfarçados para invadir o inimigo, o líder é equivalente a um tema abstrato. O inspetor Zhang San enviou dois agentes disfarçados Li Si e Wan Wan Wu. Zhang San é equivalente a um tema específico, e o agente disfarçado é equivalente a um observador abstrato. Esses dois agentes disfarçados são Li Si e Wang Wu, que são observadores específicos. A ação do inquérito é equivalente ao observador que registra o assunto. Então este diagrama de classe é o seguinte:
Usando a API Java para implementar a descrição do código da seguinte forma:
Pacote Observer; importar java.util.list; importar java.util.observable; importar java.util.observable; / ***Descrição: Polícia Zhang San*/ Polícia de classe pública estende observável {private String Time; Polícia pública (Lista <Teverver> List) {super (); for (observador o: list) {addobserver (o); }} public void alteração (string time) {this.time = time; setChanged (); notifyObServers (this.time); }} Pacote Observer; importar java.util.observable; importar java.util.observer; / ** *Descrição: Undercover a */ public class Undercovera implementa observador {private string time; @Override public void update (observável o, objeto arg) {time = (string) arg; System.out.println ("Descobrir uma mensagem recebeu uma mensagem e o tempo de ação é:"+tempo); }} Pacote Observer; importar java.util.observable; importar java.util.observer; / ** *Descrição: Undercover b */ public class Undercoverb implementa observador {private string time; @Override public void update (observável o, objeto arg) {time = (string) arg; System.out.println ("Uncover b recebeu uma mensagem e o tempo de ação foi:"+tempo); }} Pacote Observer; importar java.util.arraylist; importar java.util.list; importar java.util.observer; / ***Descrição: teste*/ public class Client {/ ***@param args*/ public static void main (string [] args) {subcovera o1 = new Undercovera (); Undercoverb O2 = novo Undercoverb (); List <Beverver> list = new ArrayList <> (); list.add (O1); list.add (O2); Assunto da polícia = nova polícia (lista); sujeito.Change ("02:25"); System.out.println ("======================= Devido às informações expostas, o tempo de ação é Avançado ============================================================================= Resultados de execução de teste:
O Undercover B recebeu a mensagem e o tempo de ação é: 02:25 Undercover a recebeu a mensagem, e o tempo de ação é: 02:25 =============== Devido à exposição da mensagem, o tempo de ação é adiantado = 01.
4. Resumo
O padrão do observador define uma relação um para muitos entre objetos. Quando o estado de um objeto (o observador) muda, os objetos que dependem dele serão notificados. Ele pode ser aplicado para publicar assinatura, alteração-update-update em tais cenários de negócios.
O observador usa um método de acoplamento solto. O observador não conhece os detalhes do observador, mas sabe apenas que o observador implementou a interface.
O modelo orientado a eventos é mais flexível, mas também paga o custo da complexidade do sistema, porque precisamos personalizar um ouvinte e um evento para cada fonte de evento, o que aumentará a carga no sistema.
O núcleo do modelo Observer é primeiro distinguir o papel e posicionar o observador e o observador, e eles são um relacionamento de muitos para um. A chave para a implementação é estabelecer uma conexão entre o observador e o observador. Por exemplo, há um conjunto na classe Observer que é usada para armazenar o observador, e todos os observadores devem ser notificados quando a coisa detectada mudar. No construtor do observador, ele será passado pelo observador e também se registrará na lista de observadores de propriedade do Observer, ou seja, a lista de observadores.
1. Vantagens do modo de observador:
(1) Os temas abstratos dependem apenas de observadores abstratos (2) o modo de observador suporta a comunicação de transmissão (3) o modo de observador separa a camada de geração de informações e a camada de resposta
2. Desvantagens do modo Observador:
(1) Se um tópico for registrado por um grande número de observadores, ele custará um preço mais alto para notificar todos os observadores (2) se o método de resposta de alguns observadores for bloqueado, todo o processo de notificação será bloqueado e outros observadores não puderem ser notificados no tempo.