2012-07-23 15 views
32

Me he dado cuenta de que mi código inyectado de dependencia con patrón de observador (usando el EventBus de Guava) a menudo es mucho más difícil de depurar que el código que he escrito en el pasado sin estas características. Particularmente cuando se trata de determinar cuándo y por qué se llama al código de observador.¿Por qué el patrón del observador debe ser desaprobado?

Martin Oderski y sus amigos escribieron un extenso artículo con un título especialmente atractivo, "Deprecating the Observer Pattern" y todavía no me he tomado el tiempo de leerlo.

Me gustaría saber qué hay de malo en el patrón del observador y mucho mejor acerca de las alternativas (propuestas u otras) para que las personas tan brillantes escriban este documento.

Para empezar, encontré una (entretenida) crítica del artículo here.

+3

Discutido aquí también ... http://lambda-the-ultimate.org/node/4028 –

Respuesta

28

Citando directamente de the paper:

para ilustrar los problemas concretos del patrón de observador, empezamos con una simple y ubicua ejemplo: arrastre de ratón. El siguiente ejemplo rastrea los movimientos del mouse durante una operación de arrastre en un objeto Path y muestra en la pantalla. Para simplificar, utilizamos los cierres Scala como observadores.

var path: Path = null 
val moveObserver = { (event: MouseEvent) => 
    path.lineTo(event.position) 
    draw(path) 
} 
control.addMouseDownObserver { event => 
    path = new Path(event.position) 
    control.addMouseMoveObserver(moveObserver) 
} 
control.addMouseUpObserver { event => 
    control.removeMouseMoveObserver(moveObserver) 
    path.close() 
    draw(path) 
} 

En el ejemplo anterior, y como vamos a discutir el observador patrón como se define en [25], en general, viola una impresionante gama de importantes principios de la ingeniería de software:

Efectos secundarios Los observadores promueven efectos secundarios. Como los observadores son apátridas, a menudo necesitamos varios de ellos para simular una máquina de estado como en el ejemplo de arrastre. Tenemos que guardar el estado donde es accesible para todos los observadores involucrados como en la variable path anterior.

encapsulación Como la variable de estado path escapa del alcance de los observadores, el patrón de observador rompe la encapsulación.

Compuestabilidad observadores múltiples forman un conjunto disperso de objetos que tienen que ver con una sola preocupación (o múltiple, ver siguiente punto). Dado que múltiples observadores están instalados en puntos diferentes en diferentes momentos, no podemos, por ejemplo, disponerlos fácilmente.

separación de las preocupaciones Los observadores anteriores no sólo pequeñas el camino del ratón, sino también llamar a un comando de dibujo, o más generalmente, incluyen dos preocupaciones diferentes en la misma ubicación del código . A menudo es preferible separar las preocupaciones de construir la ruta y mostrarla, por ejemplo, como en el patrón model-view-controller (MVC) [30].

Escalabilidad Podríamos conseguir una separación de preocupaciones en nuestro ejemplo mediante la creación de una clase para sí mismo caminos que publica eventos cuando cambia la trayectoria. Lamentablemente, no existe una garantía para la coherencia de los datos en el patrón del observador. Supongamos que creamos otro evento publicando el objeto que depende de los cambios en nuestra ruta original, por ejemplo, un rectángulo que representa los límites de nuestra ruta. También considera que un observador está escuchando los cambios tanto en la ruta como en sus límites para dibujar una ruta enmarcada. Este observador debería determinar manualmente si los límites ya están actualizados y, de lo contrario, diferir el funcionamiento del dibujo . De lo contrario, el usuario podría observar un marco en la pantalla que tiene el tamaño incorrecto (un error).

Uniformidad Diferentes métodos para instalar diferentes observadores Disminuir la uniformidad del código.

Abstracción Hay un nivel bajo de abstracción en el ejemplo. Se basa en una interfaz de peso pesado de una clase de control que proporciona más que solo métodos específicos para instalar observadores de eventos de mouse. Por lo tanto, no podemos abstraer sobre las fuentes de eventos precisos. Por ejemplo, podría permitir al usuario abortar una operación de arrastre presionando la tecla escape o usar un dispositivo puntero diferente, como una pantalla táctil o una tableta gráfica.

Gestión de recursos La vida de un observador debe ser administrado por clientes. Por motivos de rendimiento, queremos observar los eventos de movimiento del mouse solo durante una operación de arrastre . Por lo tanto, necesitamos instalar explícitamente y desinstalar el mouse move observer y necesitamos recordar el punto de instalación (control anterior).

distancia semántica En última instancia, el ejemplo es difícil de entender porque el flujo de control se invierte lo que resulta en demasiado código repetitivo que aumenta la semántica distancia entre el programadores intención y el código real.

[25] E. Gamma, R. Helm, R. Johnson y J. Vlissides. Diseño patrones: elementos de software orientado a objetos reutilizables. Addison-Wesley Longman Publishing Co., Inc., Boston, MA, EE.UU., 1995. ISBN 0-201-63361-2.

6

Creo que el patrón Observer tiene los inconvenientes estándar que acompañan a la disociación de las cosas. El sujeto se desacopla de Observer pero no puedes simplemente mirar su código fuente y descubrir quién lo observa. Las dependencias codificadas normalmente son más fáciles de leer y pensar, pero son más difíciles de modificar y reutilizar. Es una compensación.

En cuanto al papel, no trata el patrón Observer en sí mismo sino un uso particular del mismo. En particular: se observan múltiples objetos Observer sin estado por cada objeto individual. Esto tiene el inconveniente obvio de que los observadores independientes necesitan sincronizarse entre sí ("Dado que los observadores son apátridas, a menudo necesitamos varios de ellos para simular una máquina de estado como en el ejemplo de arrastre. Tenemos que guardar el estado en el que es accesible para todos los observadores involucrados como en la ruta variable anterior. ")

El inconveniente anterior es específico para este tipo de uso, no para el patrón Observer en sí. También podría crear un único objeto observador (¡con estado!) Que implemente todos los métodos OnThis, OnThat, OnWhatever y elimine el problema de simular una máquina de estados en muchos objetos sin estado.

+0

Pero los observadores en el ejemplo * do * claramente tienen efectos secundarios como se menciona en su presupuesto. (Ver todas las manipulaciones de la definición 'path'). Aquí –

6

Seré breve porque soy nuevo en el tema (y aún no leí ese artículo específico).

Patrón de observador es intuitivamente incorrecto: el Objeto que debe observarse sabe quién está observando (Sujeto <> - Observer). Eso está en contra de la vida real (en escenarios basados ​​en eventos). Si grito, no tengo idea de quién está escuchando; si un rayo cae al suelo ... ¡un rayo no sabe que hay un piso hasta que golpea !. Solo los observadores saben lo que pueden observar.

Cuando este tipo de cosas suceden, el software suele ser un desastre, porque se construye en contra de nuestra forma de pensar. Es como si y el objeto supiera lo que otros objetos pueden llamar sus métodos.

IMO una capa como "Medio Ambiente" es el encargado de tomar los eventos y notificar al afectado. (O mezcla el evento y el generador de ese evento)

Event-Source (Subject) genera eventos para el entorno. El entorno entrega el evento al observador. El observador puede registrarse en el tipo de eventos que lo afectan o que realmente está definido en el entorno. Ambas posibilidades tienen sentido (pero yo quería ser breve).

En mi comprensión, el Observer Pattern reúne el entorno & subject.

PS. odio poner en los párrafos ideas abstractas! : P

+0

http://www.marco.panizza.name/dispenseTM/slides/exerc/eventNotifier/eventNotifier.html he encontrado el diagrama que básicamente dice lo mismo: -Publisher (Asunto) -Abonos (Objeto) -EventService (Environment) (1998) Después de todo, el "sentido común" lo conduce y de una forma u otra implementamos este tipo de capa intermedia cuando programamos. El problema surge cuando el Patrón motiva o induce a error. –

+0

Los sujetos no conocen a sus observadores (quizás solo a través de la clase base). El libro Design Pattern también menciona un administrador de cambios. Esto es algo similar a tu entorno. El patrón Imho the Observer es ideal para la notificación de elementos GUI (independientes), pero el patrón también presenta serios inconvenientes: notificaciones falsas mientras el sujeto o la capa empresarial no está en un estado consistente; cambios de sujeto por un observador durante una actualización de notificación. – gast128

+0

Algunos puntos aquí, el sujeto no sabe nada sobre los observadores, podemos lograr esto mediante una interfaz abstracta. –

Cuestiones relacionadas