1. Definición de modo observador:
El modo Observer también se llama modo de publicación de suscripción, en el que un objeto objetivo gestiona todos los objetos observadores que dependen de él y emiten notificaciones activamente cuando su estado cambia. Esto generalmente se logra llamando a los métodos de cada observador. Este patrón generalmente se usa en los sistemas de procesamiento de eventos.
1. Estructura general del patrón de observador
Primero, echemos un vistazo a la descripción del diagrama de clases del patrón del observador:
Los roles del modo observador son los siguientes:
Asunto (interfaz de tema abstracto): define una serie de operaciones en la lista de observadores en la clase temática, incluida la adición, eliminación, notificación, etc.
Asunto concreto (clase de tema específica):
Observador (interfaz de observador abstracto): define el observador para aceptar operaciones en el estado de actualización de la clase de tema.
ConcreteBServer (clase de observador específica): implementa la lógica como las notificaciones de clase de temas de actualización por parte de la interfaz Observer.
De este diagrama de clase, podemos ver que la clase temática mantiene una lista de clases que implementa la interfaz Observador. La clase temática utiliza esta lista para realizar una serie de adiciones, deleciones y modificaciones en el observador. La clase Observer también puede llamar activamente el método de actualización para comprender la información de actualización de estado de la clase temática.
Los diagramas de clase anteriores describen solo la idea básica del patrón del observador, y hay muchas deficiencias. Por ejemplo, como observador, también puede suscribirse activamente a ciertos temas, etc. Los siguientes ejemplos realizarán algunos cambios para aplicar una lógica comercial específica.
2. Ejemplo de modo observador
Construimos una clase de observador y tema, donde los observadores pueden suscribirse activamente o cancelar temas. La categoría de temas es administrada de manera uniforme por un gerente de tema. El siguiente es un diagrama de clase:
Sujeto:
Asunto de interfaz pública {// Registro un registro de observador público vacío (Observer Observer); // eliminar un observador público vacío eliminar (observador observador); // notificar a todos los observadores public void notifyObServers (); // Obtenga el mensaje para ser publicado por la clase Topic Public String getMessage ();} concertesubject: public class mySubject implementa sujeto {Lista privada <Sobrerver> Observadores; Boolean privado cambiado; mensaje de cadena privada; // bloqueo de objeto, utilizado para actualizar sincrónicamente la lista de observadores, el objeto final privado mutex = new Object (); public mySubject () {observadores = new ArrayList <Smanterver> (); cambiado = falso; } @Override public void Register (Observer Observer) {if (Observer == NULL) Throw New NULLPOInterException (); // certificar no repetir if (! Observvers.contains (observador)) observadores.add (observador); } @Override public void eliminar (observador observador) {observadores.remove (observador); } @Override public void NotifyObServers () {// Temp List List <Sobserver> TempOBServers = null; sincronizado (mutex) {if (! cambiado) return; tempobServers = new ArrayList <> (this.observers); this.changed = false; } para (observador obj: tempobServers) {obj.update (); }} // La clase de temas publica un nuevo mensaje public void makechanged (mensaje de cadena) {System.out.println ("El sujeto realiza un cambio:" + mensaje); this.message = Mensaje; this.changed = true; notifyObServers (); } @Override public String getMessage () {return this.message; }}Cuando Concertesubject realiza una actualización, todos los observadores en la lista son notificados y se llama al método de actualización del observador para implementar la lógica después de recibir la notificación. Tenga en cuenta el bloque de sincronización en NotifyObServers aquí. En el caso de múltiples subprocesos, para evitar agregar y eliminar la lista de observadores por otros hilos al publicar notificaciones en la clase de tema, se usa una lista temporal en el bloque de sincronización para obtener la lista de Observador actual.
Sujeto Management: Gerente de clase temática
Public Class SujetManagement {// un nombre de registro - mapa de mapa privado <string, stater> tastList = new HashMap <String, Sujem> (); public void addSubject (nombre de cadena, sujeto sujeto) {tastlist.put (nombre, asunto); } public void addSubject (sujeto sujeto) {tastlist.put (satert.getClass (). getName (), sujeto); } sujeto público getSubject (string temasname) {return teaTList.get (temasname); } public void RemoveSubject (nombre de cadena, sujeto sujeto) {} public void RemoveSubject (sujeto sujeto) {} // singleton private stertmanagement () {} public static statmmanagement getInStance () {return SematerManagementInstance.Instance; } Clase estática privada Sujeto ManagementIntance {instancia final de sujana final de sujeción = new SematManagement (); }}El propósito del gerente del tema es obtener un objeto de instancia del tema cuando el observador se suscribe a un tema.
Observador:
Public Interface Observer {public void Update (); public void setSubject (sujeto sujeto);} ConceptEobServer: clase pública myobserver implementa observador {sujeto privado sujeto; // Obtener el mensaje de notificación del tema concentrado @Override public void Update () {String Message = SUST.GetMessage (); System.out.println ("From Sujem" + SUMT.GetClass (). GetName () + "Mensaje:" + Mensaje); } @Override public void setSubject (sujeto sujeto) {this.subject = sujeto; } // subcirbe algún tema público vacío suscription (string SujetName) {Sujemmanagement.getInStance (). } // Cancelar suscripción public void cancelSubScribe (String SujetName) {Sujemmanagement.getInStance (). }} Prueba: Abstractamos las clases y observadores de temas en escritores y lectores
clase pública ObserVerTest {escritor privado de mysubject static; @Beforeclass public static void setupbeforeclass () lanza excepción {Writer = new MySubject (); // Agregar un escritor llamado Linus SujetManagement.getInstance (). AddSubject ("Linus", escritor); } @Test public void test () {// Definir varios lectores myobserver lector1 = new MyobServer (); Myobserver lector2 = new MyobServer (); Myobserver lector3 = new MyobServer (); Reader1.setsubject (escritor); lector2.setsubject (escritor); lector3.setsubject (escritor); lector1.subscribe ("Linus"); lector2.subscribe ("Linus"); lector3.subscribe ("Linus"); escritor.makechanged ("Tengo un nuevo cambio"); lector1.update (); }}Lo anterior es un pequeño ejemplo del patrón del observador. Se puede ver que cada clase de tema debe mantener una lista de observadores correspondiente. Aquí, podemos abstragar más basado en el nivel abstracto del tema específico, poner esta reunión en una clase abstracta para mantener conjuntamente una lista. Por supuesto, la operación específica depende de la lógica comercial real.
2. Oyente en Servlet
Hablemos del oyente en Servlet, hablemos de otra forma del patrón de observador: el modelo basado en eventos. Al igual que el papel temático del patrón Observer mencionado anteriormente, el modelo basado en eventos incluye fuentes de eventos, eventos específicos, oyentes y oyentes específicos.
El oyente en Servlet es un modelo típico basado en eventos.
Hay un conjunto de clases basadas en eventos en JDK, incluida una interfaz de oyente unificada y una fuente de eventos unificados. El código fuente es el siguiente:
/*** Una interfaz de etiquetado que deben extenderse todas las interfaces del oyente de eventos. * @Since JDK1.1 */Public Interface EventListener {}Esta es una interfaz de bandera, y el JDK estipula que todos los oyentes deben heredar esta interfaz.
Public Class EventObject implementa java.io.serializable {private static final long SerialVersionUid = 5516075349620653480l; /*** El objeto en el que ocurrió inicialmente el evento. */ fuente de objeto transitorio protegido; /*** Construye un evento prototipo. * * @param Fuente El objeto en el que ocurrió inicialmente el evento. * @Exception ilegalArgumentException si la fuente es nula. */ public EventObject (fuente de objeto) {if (fuente == NULL) Lanzar nueva IllegalArgumentException ("Fuente nulo"); this.source = fuente; } /*** El objeto en el que ocurrió inicialmente el evento. * * @return el objeto en el que ocurrió inicialmente el evento. */ Public Object getSource () {Fuente de retorno; } /*** Devuelve una representación de cadena de este EventObject. * * @return una representación de una cadena de este evento EventObject. */ public String toString () {return getClass (). getName () + "[fuente =" + fuente + "]"; }}EvenObject es una fuente de evento unificada especificada por JDK. La clase EvenObject define una fuente de evento y un método GET para obtener la fuente del evento.
Analicemos el proceso de operación del oyente de servlet.
1. La composición del oyente de servlet
Actualmente, hay 6 tipos de interfaces de oyentes para dos tipos de eventos en servlets, como se muestra en la figura a continuación:
La situación de activación específica es la siguiente:
2. Un proceso de activación de oyente específico
Tomemos ServLetRequestAtTributeListener como ejemplo para analizar el proceso basado en eventos aquí.
En primer lugar, cuando httpservletRequest llama al método setattrilbute, en realidad se llama org.apache.catalina.connector.request#setattrilbute método. Echemos un vistazo a su código fuente:
public void setAttribute (nombre de cadena, valor de objeto) {... // El código lógico anterior se ha omitido // aquí el oyente es notifyattributeaseSigned (nombre, valor, OldValue); }El siguiente es el código fuente de NotifyAtTributesSigned (nombre de cadena, valor de objeto, objeto OldValue)
Private void notifyAttributessigned (nombre de cadena, valor de objeto, objeto OldValue) {// Obtenga el objeto de instancia del oyente definido en la aplicación web de los oyentes de objeto de contenedor [] = context.getApplicationEventListeners (); if ((oyentes == null) || (oyentes.length == 0)) {return; } boolean reemplazado = (OldValue! = NULL); // Crear objeto de evento relacionado ServletRequestatTributeEvent Event = null; if (reemplazado) {event = new ServLetRequestAtTributeEvent (context.getServletContext (), getRequest (), name, OldValue); } else {event = new ServLetReSquestAtTribiTEEVEvent (context.getServletContext (), getRequest (), nombre, valor); } // Tranquilidad a través de todas las listas de oyentes y busque el oyente para el evento correspondiente para (int i = 0; i <oyente.length; i ++) {if (! (Oyentes [i] instancia de servletrequestattributeListener)) {continuar; } // Llamando al método del oyente para implementar la operación de escucha ServLetRequestatTributEnstener oyente = (ServLetRequestatTributEnstener) oyentes [i]; intente {if (reemplazado) {oyente.attributeRePlaced (evento); } else {LOYER.ATTRUTEADDED (evento); }} catch (showable t) {excepcionUtils.handlethrowable (t); context.getLogger (). Error (SM.GetString ("Coyoterequest.AttriTeEvent"), t); // La válvula de error elegirá esta excepción y la mostrará como Atributes de usuario.put (requestdispatcher.error_exception, t); }}}El ejemplo anterior muestra claramente cómo se llama ServLetRequestAtTributeListener. Los usuarios solo necesitan implementar la interfaz del oyente. Los oyentes en servlets casi cubren eventos en los que le interesan durante todo el ciclo de vida del servlet. El uso flexible de estos oyentes puede hacer que el programa sea más flexible.
3. Ejemplos completos
Por ejemplo, si ha visto las películas de policía y gángsters de TVB, sabrá cómo funciona el encubierto. En general, un policía puede tener varios agentes encubiertos que se colan en el enemigo y preguntan sobre la información. El agente encubierto trabaja completamente en las instrucciones de su líder. El líder dice qué acción debe seguir. Si el tiempo de acción cambia, debe cambiar inmediatamente el tiempo que coopera con la acción. Si el líder envía dos agentes encubiertos para invadir al enemigo, entonces el líder es equivalente a un tema abstracto. El inspector Zhang San envió dos agentes encubiertos Li Si y Wan Wang Wu. Zhang SAN es equivalente a un tema específico, y el agente encubierto es equivalente a un observador abstracto. Estos dos agentes encubiertos son Li Si y Wang Wu, que son observadores específicos. La acción de la investigación es equivalente al observador que registra el tema. Entonces este diagrama de clase es el siguiente:
Uso de la API Java para implementar la descripción del código de la siguiente manera:
Observador de paquetes; import java.util.list; import java.util.observable; import java.util.observable; / ***Descripción: Police Zhang San*/ Public Class Police extiende observable {tiempo de cadena privada; Policía pública (Lista <Scarserver> List) {super (); for (observador o: list) {addObServer (o); }} public void Change (String Time) {this.time = time; setchanged (); notifyObServers (this.time); }} Observador de paquetes; import java.util.observable; import java.util.observer; / ** *Descripción: Undercover a */ public class UnderCoversa implementa observador {tiempo de cadena privada; @Override public void Update (observable o, objeto arg) {time = (string) arg; System.out.println ("descubrir un mensaje recibido, y el tiempo de acción es:"+tiempo); }} Observador de paquetes; import java.util.observable; import java.util.observer; / ** *Descripción: Undercover b */ public class UnderCoverb implementa observador {tiempo de cadena privada; @Override public void Update (observable o, objeto arg) {time = (string) arg; System.out.println ("descubrir B recibió un mensaje, y el tiempo de acción fue:"+tiempo); }} Observador de paquetes; import java.util.arrayList; import java.util.list; import java.util.observer; / ***Descripción: Test*/ public class Client {/ ***@param args*/ public static void main (string [] args) {subcoversa o1 = new Unscoversa (); Subcubreve O2 = new UnderCoverb (); List <Smanterver> list = new ArrayList <> (); list.add (O1); list.add (O2); Asunto de la policía = nueva policía (lista); Sujeto.change ("02:25"); System.out.println ("======================= debido a la información expuesta, el tiempo de acción es Avanzado ==============================================================================. Resultados de la ejecución de la prueba:
Encubierto b recibió el mensaje, y el tiempo de acción es: 02:25 Undercover A recibió el mensaje, y el tiempo de acción es: 02:25 ================ Debido a la exposición del mensaje, la hora de acción está antes ============ SECURNE.
4. Resumen
El patrón del observador define una relación de uno a muchos entre objetos. Cuando cambia el estado de un objeto (el observador), se notificarán los objetos que dependen de él. Se puede aplicar para publicar suscripción, cambiar de actualización de actualización en tales escenarios comerciales.
El observador utiliza un método de acoplamiento suelto. El observador no conoce los detalles del observador, pero solo sabe que el observador ha implementado la interfaz.
El modelo basado en eventos es más flexible, pero también paga el costo de la complejidad del sistema, porque tenemos que personalizar un oyente y un evento para cada fuente de eventos, lo que aumentará la carga del sistema.
El núcleo del modelo observador es distinguir primero el papel y posicionar al observador y al observador, y son una relación de muchos a uno. La clave para la implementación es establecer una conexión entre el observador y el observador. Por ejemplo, hay un conjunto en la clase Observador que se usa para almacenar el observador, y todos los observadores deben ser notificados cuando cambia la cosa detectada. En el constructor del observador, el observador lo pasará y también se registrará en la lista de observadores propiedad del observador, es decir, la lista de observadores.
1. Ventajas del modo Observador:
(1) Los temas abstractos solo confían en los observadores abstractos (2) el modo Observador admite la comunicación de transmisión (3) el modo observador separa la capa de generación de información y la capa de respuesta
2. Desventajas del modo Observador:
(1) Si un tema está registrado por una gran cantidad de observadores, costará un precio más alto notificar a todos los observadores (2) Si el método de respuesta de algunos observadores está bloqueado, todo el proceso de notificación se bloqueará y otros observadores no se pueden notificar a tiempo.