Patrones de Diseo Patrn de comportamiento Observer Observer Propsito Define una dependencia de uno-a-muchos entre objetos de forma que, cuando un objeto cambia de estado, se notifica a los objetos dependientes para que se actualicen automticamente. Tambin conocido como dependents, publish- subscribe Observer Motivacin Mantener la consistencia entre objetos relacionados, sin aumentar el acoplamiento entre clases Ej: separacin de la capa de presentacin en una interfaz de usuario de los datos de aplicacin subyacentes A B C D 0 10 20 30 40 A B C D a: 40% b: 25% c: 15% d: 20% A B C D X 60 20 15 5 Y 40 25 15 20 Z 10 10 20 60 Not i f i caci n de cambi o Consul t a, modi f i caci n Observer Aplicabilidad Usa el patrn Observer: Cuando una abstraccin tiene dos aspectos, y uno depende del otro. Encapsular los aspectos en objetos distintos permite cambiarlos y reutilizarlos. Cuando cambiar un objeto implica cambiar otros, pero no sabemos exactamente cuntos hay que cambiar Cuando un objeto debe ser capaz de notificar algo a otros sin hacer suposiciones sobre quines son dichos objetos. Esto es, cuando se quiere bajo acoplamiento. Observer Estructura Observer + update() Subject +attach(Observer) +detach (Observer) +notify () forall o in observers: o.update(); ConcreteSubject - subjectState +getState() +setState() ConcreteObserver - observerState +update() return subjectState; observers * 0..1 subject 0..1 observerState =subject.getState(); Observer Participantes Subject: conoce a sus observadores, que pueden ser un nmero arbitrario proporciona una interfaz para aadir y quitar objetos observadores Observer: define la interfaz de los objetos a los que se deben notificar cambios en un sujeto ConcreteSubject: almacena el estado de inters para sus observadores enva notificaciones a sus observadores cuando su estado cambia ConcreteObserver: mantiene una referencia a un ConcreteSubject almacena el estado del sujeto que le resulta de inters implementa la interfaz de Observer para mantener su estado consistente con el del sujeto Observer Colaboraciones :ConcreteSubject co1 :ConcreteObserver co2 :ConcreteObserver setState() notify() update() getState() getState() update() (sujeto con dos observadores) Observer Colaboraciones :ConcreteSubject co :ConcreteObserver setState() notify() update() getState() (sujeto con un nmero arbitrario de observadores) loop [for each co in observers] Observer Consecuencias Permite modificar sujetos y observadores de manera independiente Permite reutilizar un sujeto sin reutilizar sus observadores, y viceversa Permite aadir observadores sin tener que cambiar el sujeto ni los dems observadores Acoplamiento abstracto entre el sujeto y el observador. El sujeto no sabe la clase concreta de sus observadores (acoplamiento mnimo). Soporte para broadcast. El sujeto enva la notificacin a todos los observadores suscritos. Se pueden aadir/quitar observadores. Actualizaciones inesperadas. Una operacin en el sujeto puede desencadenar una cascada de cambios en sus observadores. El protocolo no ofrece detalles sobre lo que ha cambiado. Observer Implementacin Correspondencia entre sujetos y observadores Usualmente, el sujeto guarda una referencia a sus observadores Costoso si hay muchos sujetos y pocos observadores. En ese caso: usar tabla hash: menor coste en espacio, pero mayor coste en tiempo Observar ms de un sujeto (ej. hoja de clculo con 2 fuentes de datos) Extender la interfaz de actualizacin para que el observador sepa qu sujeto cambi de estado (por ej. pasar el sujeto en la llamada a update). s1:Sujeto s2:Sujeto s3:Sujeto s4:Sujeto o:Observador hash intermedia (s1, s3 y s4 no incurren en gasto de almacenamiento de observadores) Observer Implementacin Quin dispara la actualizacin llamando a notify? El sujeto desde aquellos mtodos que cambian su estado ventaja: las clases cliente no tienen que hacer nada inconveniente: no es ptimo si hay varios cambios de estado seguidos Las clases cliente ventaja: se puede optimizar llamando a notify tras varios cambios inconveniente: los clientes tienen la responsabilidad de llamar a notify Referencias perdidas a sujetos que se han eliminado Se puede evitar notificando la eliminacin del sujeto a sus observadores En general, eliminar los observadores del sujeto eliminado no es una opcin Observer Implementacin Asegurarse de la consistencia del sujeto antes de una notificacin Cuidado con las operaciones heredadas! cl ass Cl aseSuj et oBase { voi d oper aci on ( i nt val or ) { _mi Var += val or ; / / act ual i za el est ado not i f y( ) ; / / di spar a l a not i f i caci n } } cl ass Mi Suj et o ext ends Cl aseSuj et oBase { voi d oper aci on ( i nt val or ) { super . oper aci on( val or ) ; / / di spar a l a not i f i caci n _mi Var += val or ; / / act ual i za el est ado ( t ar de) } } Soluciones: documentar los mtodos que envan notificaciones patrn de diseo Template Method Observer Implementacin Asegurarse de la consistencia del sujeto antes de una notificacin Soluciones: documentar los mtodos que envan notificaciones patrn de diseo Template Method cl ass Cl aseSuj et oBase { voi d oper aci on ( i nt val or ) { _mi Var += val or ; } voi d oper aci onNot i f y ( i nt val or ) { sel f . oper aci on( val or ) ; / / ej ecut a l a oper aci n sel f . not i f y( ) ; / / di spar a l a not i f i caci n } } cl ass Mi Suj et o ext ends Cl aseSuj et oBase { voi d oper aci on ( i nt val or ) { super . oper aci on( val or ) ; _mi Var += val or ; } } los clientes llaman a oper aci onNot i f y en vez de a oper aci on Observer Implementacin Evitar protocolos de actualizacin especficos del observador Modelo pull: el sujeto enva lo mnimo, los observadores piden lo necesario inconveniente: puede ser poco eficiente Modelo push: el sujeto enva informacin detallada del cambio, aunque los observadores no la necesiten. inconveniente: observadores menos reutilizables Especificar las modificaciones de inters explcitamente Hacer update ms eficiente, haciendo que los observadores se registren slo en los eventos que les interesan Los observadores se subscriben a aspectos del sujeto cl ass Subj ect { publ i c at t ach ( Obser ver o, Aspect a) { . . . } } cl ass Obser ver { publ i c updat e ( Subj ect s, Aspect a) { . . . } } Observer Implementacin Encapsular la semntica de actualizaciones complejas Cuando la relacin de dependencia entre sujetos y observadores es compleja, se puede usar un objeto intermedio para la gestin de cambios Minimiza el trabajo de reflejar cambios de los sujetos en los observadores Ej.: si se actualizan varios sujetos, hay que asegurar que los observadores se actualizan slo despus del cambio en el ltimo sujeto Subject +attach (Observer o) +detach (Observer) +notify () Observer + update(Subject) subjects * * chman 1 ChangeManager + register (Subject,Observer) + unregister (Subject,Observer) + notify () Subject-Observer mapping observers chman.register(this,o); SimpleChangeManager +register (Subject,Observer) +unregister (Subject,Observer) +notify () DAGChangeManager +register (Subject,Observer) +unregister (Subject,Observer) +notify () forall s in subjects: forall o in s.observers: o.update(s) chman.notify(); Marcar observers a actualizar. Actualizar observers marcados. 1 Observer Ejemplo Observer + update() Subject +attach(Observer) +detach (Observer) +notify () Datasource - a, b, c, d: double +getState(): double[4] +setState(double[4]): void Fila - data: double[4] +update() observers * 1 1 * Barras - a, b, c, d: double +update() Circular - a, b, c, d: double +update() * * 1 1 Observer Cdigo de ejemplo publ i c abst r act cl ass Subj ect { pr ot ect ed Li st <Obser ver > _obser ver s; publ i c Subj ect ( ) { _obser ver s = new Li nkedLi st <Obser ver >( ) ; } publ i c voi d at t ach( Obser ver o) { _obser ver s. add( o) ; } publ i c voi d det ach( Obser ver o) { _obser ver s. r emove( o) ; } publ i c voi d not i f y( ) { I t er at or <Obser ver > i t ; i t = _obser ver s. i t er at or ( ) ; whi l e ( i t . hasNext ( ) ) i t . next ( ) . updat e( ) ; } } publ i c cl ass Dat asour ce ext ends Subj ect { pr i vat e doubl e _a, _b, _c, _d; publ i c doubl e[ ] get St at e ( ) { doubl e[ ] d = new doubl e[ 4] ; d[ 0] = _a; d[ 1] = _b; d[ 2] = _c; d[ 3] = _d; r et ur n d; } publ i c voi d set St at e( doubl e[ ] d) { _a = d[ 0] ; _b = d[ 1] ; _c = d[ 2] ; _d = d[ 3] ; t hi s. not i f y( ) ; } } Observer Cdigo de ejemplo publ i c abst r act cl ass Obser ver { pr ot ect ed Subj ect _subj ect ; publ i c Obser ver ( Subj ect s) { _subj ect = s; _subj ect . at t ach( t hi s) ; } publ i c abst r act voi d updat e ( ) ; } publ i c cl ass Fi l a ext ends Obser ver { pr i vat e doubl e[ ] _dat a; publ i c Fi l a ( Subj ect s) { super ( s) ; _dat a = new doubl e[ 4] ; } publ i c voi d updat e ( ) { doubl e[ 4] dat a; dat a = ( ( Dat asour ce) _subj ect ) . get St at e( ) ; f or ( i nt i =0; i <4; i ++) _dat a[ i ] = dat a[ i ] ; t hi s. r edr aw( ) ; } publ i c voi d r edr aw ( ) { . . . } } Observer Cdigo de ejemplo publ i c abst r act cl ass Obser ver { publ i c abst r act voi d updat e ( ) ; } publ i c cl ass Fi l a ext ends Obser ver { pr i vat e Dat asour ce _subj ect ; pr i vat e doubl e[ ] _dat a; publ i c Fi l a ( Dat asour ce s) { _subj ect = s; _subj ect . at t ach( t hi s) ; _dat a = new doubl e[ 4] ; } publ i c voi d updat e ( ) { doubl e[ 4] dat a; dat a = _subj ect . get St at e( ) ; f or ( i nt i =0; i <4; i ++) _dat a[ i ] = dat a[ i ] ; t hi s. r edr aw( ) ; } publ i c voi d r edr aw ( ) { . . . } } Observer En java La interfaz j ava. ut i l . Obser ver voi d updat e ( Obser vabl e o, Obj ect ar g) La clase j ava. ut i l . Obser vabl e Obser vabl e( ) voi d addObser ver ( Obser ver ) i nt count Obser ver s( ) voi d del et eObser ver ( Obser ver o) voi d del et eObser ver s( ) voi d not i f yObser ver s( ) voi d not i f yObser ver s( Obj ect ar g) bool ean hasChanged( ) voi d cl ear Changed( ) voi d set Changed( )