strategy pattern observer oop design-patterns observer-pattern

oop - observer - strategy pattern



¿Por qué el patrón del observador debe ser desaprobado? (3)

Citando directamente del periódico :

Para ilustrar los problemas precisos del patrón del observador, comenzamos con un ejemplo simple y omnipresente: arrastrar el mouse. El siguiente ejemplo rastrea los movimientos del mouse durante una operación de arrastre en un objeto Path y lo muestra en la pantalla. Para mantener las cosas simples, utilizamos 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) }

El ejemplo anterior, y como argumentaremos el patrón de observador como se define en [25] en general, viola una impresionante lista de principios importantes de ingeniería de software:

Los observadores de efectos secundarios promueven los 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 sea accesible para todos los observadores involucrados, como en la path variable anterior.

Encapsulación A medida que la path variable de estado escapa al alcance de los observadores, el patrón del observador rompe la encapsulación.

Composición. Múltiples observadores forman una colección suelta de objetos que se ocupan de una sola preocupación (o múltiple, vea el siguiente punto). Debido a que múltiples observadores están instalados en diferentes puntos en momentos diferentes, no podemos, por ejemplo, disponerlos fácilmente por completo.

Separación de inquietudes Los observadores anteriores no solo rastrean la ruta del mouse sino que también llaman a un comando de dibujo o, de manera más general, incluyen dos preocupaciones diferentes en la misma ubicación de código. A menudo es preferible separar las preocupaciones de construir la ruta y mostrarla, por ejemplo, como en el patrón modelo-vista-controlador (MVC) [30].

Scalablity Podríamos lograr una separación de preocupaciones en nuestro ejemplo creando una clase para las rutas que a su vez publica eventos cuando la ruta cambia. Lamentablemente, no hay garantía de consistencia de los datos en el patrón de observadores. Supongamos que creamos otro objeto de publicación de eventos que depende de los cambios en nuestra ruta original, por ejemplo, un rectángulo que representa los límites de nuestra ruta. También considere la posibilidad de que un observador escuche los cambios tanto en el camino como en sus límites para dibujar un camino enmarcado. Este observador necesitaría determinar manualmente si los límites ya están actualizados y, de lo contrario, diferir la operación de dibujo. De lo contrario, el usuario podría observar un cuadro en la pantalla que tiene el tamaño incorrecto (una falla).

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

Abstracción Hay un bajo nivel de abstracción en el ejemplo. Se basa en una interfaz de gran peso 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 abstraernos sobre las fuentes de eventos precisos. Por ejemplo, podríamos permitir que el usuario abortara una operación de arrastre al presionar la tecla de escape o usar un dispositivo de puntero diferente, como una pantalla táctil o una tableta gráfica.

Gestión de recursos Los clientes deben gestionar la vida de un observador. Debido a razones de rendimiento, queremos observar eventos de movimiento del mouse solo durante una operación de arrastre. Por lo tanto, necesitamos instalar y desinstalar explícitamente el observador de movimiento del mouse y debemos 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 está invertido, lo que da como resultado un código repetitivo que aumenta la distancia semántica entre la intención del programador y el código real.

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

Me he dado cuenta de que mi código de patrón de observador inyectado de dependencia (utilizando 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.

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 sobre las alternativas (propuestas u otras) para llevar a este tipo de personas brillantes a escribir este artículo.

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


Creo que el patrón Observador 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 documento, no aborda 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í ( "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 sea accesible para todos los observadores involucrados, como en el camino variable anterior ").

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


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

El patrón observador es intuitivamente incorrecto: el objeto que se observará sabe quién está observando (sujeto <> - observador). 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 sucede este tipo de cosas, 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 "Ambiente" es la encargada de tomar los eventos y notificar a los afectados. (O combina 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).

Según entiendo, el patrón de observación reúne el entorno y el tema.

PD. odio poner en los párrafos ideas abstractas! :PAG