¿Debería cada objeto saber cómo presentarse / dibujarse?

8

David West en su libro Object Thinking (capítulo 10, sección 1, subsección 2) propuso que en un entorno ideal de OO, todos los objetos deberían ser capaces de presentarse a pedido; ya sea para humanos (como GUI), componentes no nativos (como JSON y / o XML), o cualquier otra parte interesada:

El pensamiento de objetos dice que una vista (a veces llamada interfaz), gráfica o de otro tipo, es un medio para que un objeto se comunique con otro objeto y nada más. La necesidad de una vista surge cuando un objeto necesita presentarse en forma "no nativa" a algún otro objeto (generalmente un ser humano) o aplicación (por ejemplo, una vista XML para objetos de datos que se comparten entre plataformas).

El descubrimiento de la necesidad y los parámetros que debe satisfacer una vista se manifiesta en los escenarios en los que participa el objeto. Cada vez que se le pide a un objeto que se muestre, debe usar una vista, una representación, apropiada para el remitente de ese mensaje. Si, por ejemplo, un objeto está tratando de instanciarse (obtener un valor para sí mismo), debe presentar una vista de sí mismo como una solicitud implícita a un ser humano (u otro objeto proveedor de servicios) de un valor. Si estamos creando una GUI que servirá como intermediario entre un objeto de software y un objeto humano, usaremos glifos para mostrar y widgets para interactuar.

Pero, ¿qué glifos y widgets deben incluirse en la GUI? Solo aquellos necesarios para completar el escenario o escenarios 4 de interés inmediato a medida que se ejecuta la aplicación. Esta perspectiva es contradictoria para la mayoría de los desarrolladores porque sugiere que se defina una GUI desde la aplicación.

Como ejemplo, considere una cervecería. A un lado hay cubas llenas de cerveza. En una línea de producción compleja que consiste en lavadoras de botellas, estaciones de llenado, máquinas tapadoras y ensambladores de paquetes. Por encima de todo, hay una estación de control que monitorea la cervecería y notifica a los gerentes humanos sobre el estado y los problemas. Es probable que los desarrolladores tradicionales comiencen su análisis y diseño de "un sistema de gestión de cervecería" desde el punto de vista del panel de control. Esto es análogo al diseño desde la interfaz en.

El pensamiento de objetos sugeriría, en cambio, que considere qué objeto es el principal cliente de la cervecería y todas sus innumerables máquinas. ¿En nombre de quién existe el complejo laberinto de equipos? La respuesta comercial correcta es, por supuesto, "El cliente". Pero una respuesta más reflexiva del pensamiento de objetos es: "La cerveza". Todos los escenarios están escritos desde la perspectiva de la cerveza, tratando de meterse en una botella, con una tapa, colocada en un paquete y residente en un camión. El panel de control es un observador pasivo 5 del estado de la cervecería. Si la cerveza encuentra un problema en algún momento, es responsabilidad de la cerveza solicitar la intervención de los operadores humanos enviando un mensaje al panel de control (o paneles de control específicos de la máquina) solicitando un servicio de intervención.

Esta perspectiva simplificará el diseño de la GUI y, lo que es más importante, eliminará la gran cantidad de objetos de administrador y controlador que parecen surgir inevitablemente al diseñar desde la perspectiva del panel de control (GUI).

Viniendo de un principiante en el mundo OO: ¿debería ser este el caso?

Tener objetos que sepan cómo representarse a sí mismos seguramente podría reducir la cantidad de objetos de controlador / administrador que West dijo repetidamente en su libro que un pensador de objetos supuestamente debería tratar de evitar a toda costa. ¿Pero acatar esta "regla" no romperá el SRP ?

Además (si resultó ser el caso), dada una implementación típica en, por ejemplo, una aplicación de Android: ¿Cómo podría uno lograr este tipo de objetivo? ¿Debería cada objeto que creamos saber presentarse como a View?

MrHadiSatrio
fuente
3
SRP no significa lo que crees que significa. SRP significa que su objeto "auto" no diagnostica problemas de mascotas. No significa que un elemento visual de la IU no deba ser capaz de mostrarse.
Robert Harvey
Veo. Sin embargo, usted menciona el elemento visual de la interfaz de usuario . Ahora, si tuviéramos que aplicar esta regla al diseñar nuestro software, ¿la mayoría de los objetos que terminaremos creando no serán elementos visuales de la interfaz de usuario ? Porque la mayoría de ellos exigirán una interacción con el usuario tarde o temprano, ¿verdad?
MrHadiSatrio
1
@RobertHarvey No estoy seguro de cómo podría verse esa cita como compatible con un patrón de tipo MVC. Se lee como si dijera que es responsabilidad de la clase definir su interfaz / "vista" para satisfacer sus necesidades, mientras que en un patrón MVC, el modelo ignoraría la vista que lo muestra.
Ben Aaronson
1
@ridsatrio Ya que eres un principiante admitido ... Te sugiero que te esfuerces por escribir interfaces que existan para decirle a un objeto algo que sucedió fuera de su control en lugar de métodos que le digan a los objetos que hagan cosas. Por lo tanto, los métodos como "buttonWasClicked" o "pageWillBeDisplayed" son mejores que "changeState" o "drawInCorner". "Dile no preguntes ..." cuenta objetos sobre lo que está sucediendo en lugar de pedirles que hagan cosas.
Daniel T.
2
En un momento dado, el autor debería haber dejado de escribir y pensar: "¿Realmente quiero enseñarle a la gente acerca de la POO hablando de la cerveza que quiere estar en una botella y les dice a los operadores cuándo no puede?" No veo cómo se supone que esto debe iluminar cualquier concepto o idea para nadie.
Sebastian Redl

Respuestas:

12

Creo que esta es una de las cosas más difíciles de entender sobre el diseño OO y, sinceramente, creo que muchos autores están equivocados al respecto y / o no lo explican muy bien. Mucha gente se equivoca y nunca se da cuenta. Tomemos un ejemplo que no está basado en GUI pero se encuentra con la misma trampa.

En Java, cada objeto tiene un método igual. Luego tiene tipos de colecciones como set y map que dependen de este método para determinar cuándo se deben agregar objetos a la colección o cuándo están duplicados. Esto parece bueno para mucha gente. El problema es que lo que termina es un objeto (la colección) cuyo comportamiento no está determinado por él sino por los objetos que contiene. Esto es un poco como tener a los pasajeros en el autobús directo a donde debe ir. ¿Qué pasa si no están de acuerdo? Este no es un problema teórico, es un problema realmente espinoso en el que básicamente tiene que romper la herencia para evitar errores en su programa. Toma una forma y una forma coloreada. ¿Es un cuadrado de 2x2 igual a un cuadrado azul de 2x2? Shape dice 'sí' y ColoredShape dice 'no'. OMS' s derecho? La respuesta depende de lo que quieras que suceda en tu colección. Es posible que ninguno de los dos dependa de lo que intente hacer.

Verás que esto surge como un problema una y otra vez. Lo curioso es que hay una solución y está justo al lado del Comparable. Los objetos que implementan Comparable tienen este mismo enigma, pero ahora tienen que determinar no solo si son iguales sino también si son más grandes que otro objeto. Es realmente intratable fuera de un ámbito de uso muy limitado. Entonces tenemos esta otra cosa llamada Comparador. Su trabajo es mirar dos objetos y decirle a la colección cuál es más grande. Todos los problemas que tiene tratando de hacer esto en el objeto Comparable desaparecen.

No conozco este libro y no conozco al autor, pero el ejemplo con la cerveza no parece útil en absoluto. ¿Cómo sabría la cerveza si debería estar en una botella o en un barril y por qué tomaría esa decisión? Su trabajo es tener buen sabor y entregar alcohol al torrente sanguíneo de los usuarios. ¿Realmente creemos que las cervecerías funcionan de esta manera? "Bien cerveza, ¿deberías estar en una botella o en un barril y si es una botella, debería ser una botella de 25 onzas o una botella de 12 onzas?" ¿Cuál es la cerveza en este caso (sin juego de palabras) de todos modos? ¿Es una gota de cerveza? Tal vez esto esté fuera de contexto, pero creo que esto se equivoca o, al menos, no agrega ninguna iluminación a este concepto.

Habiendo dicho todo eso, hay un enfoque para construir interfaces que he usado que puede simplificar las cosas y hacerlo más OO. Básicamente, crea una interfaz que define las acciones abstractas que puede realizar para mostrar el objeto. Es posible que tenga una interfaz llamada Displaymétodos como setTitleo setDescriptionsi está utilizando el patrón de nomenclatura Java estándar. Entonces tu objeto tendría un métododisplay(Display display)(¡porque tres veces es el encanto!) En este enfoque, el objeto no necesita comprender qué es la interfaz, podría ser texto, binario, svg, mapa de bits, lo que sea y la interfaz no necesita saber sobre el objeto . De esta manera, un objeto puede "mostrarse" sin necesidad de saber cómo funciona la pantalla. Este enfoque puede reducir en gran medida la cantidad de clases de contenedor necesarias, pero puede ser engorroso si tiene requisitos de visualización complejos que varían según el objeto. Puede mezclarlo con enfoques de tipo MVC estándar con buenos resultados.

JimmyJames
fuente
2
Buena respuesta, y creo que toca todos los temas relevantes.
Robert Harvey
Esta es una muy buena respuesta de hecho. Ahora que lo pienso, quizás su forma de permitir que los objetos se dibujen a través de una interfaz definida es el mejor enfoque para este asunto. De esta manera, los objetos aún tienen el derecho de controlar cómo quieren que se muestren (no mediante un controlador externo) pero sin necesidad de que aprendan cómo funcionaría un Displayen su implementación concreta (como deberían si se les diera un plano JFrame, por ejemplo ) Gracias.
MrHadiSatrio
1
Esta respuesta es increíble y me hace reflexionar mucho sobre la POO en general. Descubrí que un ECS es mucho más fácil de mantener, lo que separa los datos y la funcionalidad, y en parte porque no sufre esa analogía de lo que describiste usando objetos que intentan determinar internamente cuál es el comportamiento correcto para compararse con ellos. algo más.
8

El Principio de responsabilidad única no significa que una clase solo haga una cosa. Significa que una clase tiene una sola razón para cambiar.

Probablemente estés pensando en un método , que realmente hace una sola cosa.

Llevado a su conclusión lógica, su versión de SRP significaría que nunca podrá registrar nada, porque el registro es una responsabilidad separada.

Es mejor pensar en una clase como un tema único y bien definido . Puede tener varios métodos que apoyen ese tema, y ​​todos podrían estar haciendo cosas diferentes.

El método más fundamental para "mostrarme" es ToString, que siempre es un método en el objeto.


Dicho todo esto, cuando creamos una interfaz de usuario, generalmente fomentamos la separación de preocupaciones al proporcionar objetos cuyo único propósito es mostrar datos de otros objetos (es decir, Vistas).

Quizás un ejemplo esté en orden. Considere un sitio web PHP, utilizando una vista. Una vista simple podría verse así:

<?php
class View
{
    private $model;
    private $controller;

    public function __construct($controller,$model) {
        $this->controller = $controller;
        $this->model = $model;
    }

    public function output(){
        return "<p>" . $this->model->string . "</p>";
    }
}

En PHP, la vista siempre contiene una output()función. Para obtener la representación visual de una vista, todo lo que tiene que hacer es llamar output(), y obtendrá una cadena adecuada para mostrar en cualquier navegador moderno.

Si observa, la Vista hace referencia a un objeto llamado model. Este es el objeto que contiene los datos reales. La Vista y el Modelo están en objetos separados; Esto es lo que establece la separación de preocupaciones.

La separación de preocupaciones es importante para los sitios web, porque es lo que permite a un diseñador trabajar en el diseño de una página web aparte del programador.

Lecturas adicionales
El patrón MVC y PHP

Robert Harvey
fuente
Bien. Pero una vez que hemos separado la vista del modelo, ¿sigue siendo el trabajo del modelo proporcionar la vista? Porque eso es lo que obtengo de la cita anterior: el objeto (o modelo en su vocabulario) debería saber cómo mostrarse, ¿verdad? En cierto sentido, supongamos que tiene un Receiptobjeto en un software de POS típico, aún así llamaría Receipt.draw(Canvas)y no ReceiptView.draw(Receipt).
MrHadiSatrio
2
En MVC, es responsabilidad del Controlador elegir qué vista usar.
Robert Harvey
Pero los objetos controladores son exactamente lo que se plantea como cosas a evitar en la percepción de West en Object Thinking (y, por lo tanto, en esa cita). La cita establece explícitamente que toda la idea detrás de los "objetos que saben cómo mostrarse" es evitar los "objetos de administrador y controlador".
MrHadiSatrio
Está bien, pero creo que tendrás que limitar ese pensamiento a objetos cuyo único propósito es la visualización. Vistas, en otras palabras. Al controlador no le importa cómo se muestra la Vista; solo le importa qué vista usar. Además, MVC no es el único paradigma de visualización posible, y otros (especialmente MVVM) funcionan de manera algo diferente. Pero el principio básico de Separación de Preocupaciones permanece.
Robert Harvey
Tenga en cuenta que el libro que está leyendo es bastante antiguo para un libro de software (2004). MVC y MVVM aún no habían tenido un uso común.
Robert Harvey
1

A juzgar por las citas que pegó, está malinterpretando el texto.

El autor no dice que todos los objetos deberían poder presentarse en una interfaz de usuario. Esto sería imposible, ya que un objeto no puede saber en qué interfaz de usuario se mostrará (en una aplicación WinForms, una aplicación de Linux que utiliza XServer, como una cadena JSON, como XML, como una imagen PNG, etc.).

El punto destacado es que debe escribir vistas especializadas cuya única responsabilidad es mostrar una determinada entidad. Podría, por ejemplo, escribir una vista que represente un objeto como HTML (como lo hacen las vistas en aplicaciones MVC). Podría hacer un serializador JSON que pueda convertir un objeto en una cadena JSON. Podría hacer un objeto que convierta otros tipos de objetos en informes PDF. Todo depende.

El punto es, separar la entidad comercial de la representación visual (o serializada). Son cosas diferentes y cambian por diferentes razones.

sara
fuente
He editado mi pregunta para elaborar la cita.
MrHadiSatrio
1
Si bien admito que mi respuesta ya no parece tan relevante, no veo qué tiene que ver su pregunta con la cita. La cita parece estar hablando más en términos de API e interfaces, en lugar de representación visual. ¿Qué partes de un objeto deben ser accesibles y qué mensajes puede enviar para completar su trabajo en el dominio comercial?
sara
"Si estamos construyendo una GUI que servirá como intermediario entre un objeto de software y un objeto humano, utilizaremos glifos para mostrar y widgets para interactuar". A juzgar por esa oración, estoy bastante seguro de que la cita también implica propósitos de representación visual.
MrHadiSatrio
No hay nada allí que indique que "el objeto en cuestión debe elegir qué glifos y widgets que mejor lo representan para un contexto dado, y también debe ser responsable de representar dichos glifos y widgets". El objeto y la cosa que lo muestra no son necesariamente (no deberían ser) lo mismo.
sara