Desacoplar clases de la interfaz de usuario

27

¿Cuál es la mejor práctica cuando se trata de escribir clases que puedan tener que saber sobre la interfaz de usuario? ¿Una clase que sabe dibujar no rompería algunas de las mejores prácticas ya que depende de cuál sea la interfaz de usuario (consola, GUI, etc.)?

En muchos libros de programación me he encontrado con el ejemplo de "Forma" que muestra la herencia. La forma de la clase base tiene un método draw () en el que cada forma, como un círculo y una anulación cuadrada. Esto permite el polimorfismo. ¿Pero el método draw () no depende mucho de la interfaz de usuario? Si escribimos esta clase, por ejemplo, Win Forms, no podemos reutilizarla para una aplicación de consola o una aplicación web. ¿Es esto correcto?

La razón de la pregunta es que siempre me encuentro atascado y colgado sobre cómo generalizar las clases para que sean más útiles. Esto realmente está funcionando en mi contra y me pregunto si estoy "intentando demasiado".

Pete
fuente
¿Por qué quieres desacoplar? ¿Porque escuchaste que era lo correcto o tienes otras razones?
SoylentGray
2
Toda la "clase que sabe dibujarse" es solo un horrible y antiguo ejemplo que desearía que desapareciera. Especialmente en la pila del programador del juego =)
Patrick Hughes
2
@Chad No tengo mucha experiencia fuera de la escuela. He leído libros y realmente me gusta aprender y leer cosas nuevas sobre patrones de diseño y mejores prácticas. Entonces, sí, puedes decir que escuché que el desacoplamiento es bueno, pero también tiene sentido. Quiero escribir código que pueda usar para una aplicación WinForms de escritorio, por ejemplo, luego tomar ese código y reutilizarlo tanto como sea posible para un sitio web o incluso una aplicación de Silverlight.
Pete
@Pete - Esa es una buena respuesta.
SoylentGray
2
@Patrick: para ser justos, si estás escribiendo una Shapeclase, entonces probablemente estés escribiendo la pila de gráficos en sí, no escribiendo un cliente en la pila de gráficos.
Ken Bloom

Respuestas:

13

¿Cuál es la mejor práctica cuando se trata de escribir clases que puedan tener que saber sobre la interfaz de usuario? ¿Una clase que sabe dibujar no rompería algunas de las mejores prácticas ya que depende de cuál sea la interfaz de usuario (consola, GUI, etc.)?

Eso depende de la clase y el caso de uso. Un elemento visual que sepa dibujar no es necesariamente una violación del principio de responsabilidad única.

En muchos libros de programación me he encontrado con el ejemplo de "Forma" que muestra la herencia. La forma de la clase base tiene un método draw () en el que cada forma, como un círculo y una anulación cuadrada. Esto permite el polimorfismo. ¿Pero el método draw () no depende mucho de la interfaz de usuario?

De nuevo, no necesariamente. Si puede crear una interfaz (drawPoint, drawLine, establecer Color, etc.), puede pasar cualquier contexto para dibujar cosas en algo a la forma, por ejemplo, dentro del constructor de la forma. Esto permitiría que las formas se dibujen en una consola o en cualquier lienzo dado.

Si escribimos esta clase, por ejemplo, Win Forms, no podemos reutilizarla para una aplicación de consola o una aplicación web. ¿Es esto correcto?

Bien, eso es cierto. Si escribe un UserControl (no una clase en general) para Windows Forms, no podrá usarlo con una consola. Pero eso no es un problema. ¿Por qué esperaría que un UserControl para Windows Forms funcione con cualquier tipo de presentación? El UserControl debe hacer una cosa y hacerlo bien. Está vinculado a una cierta forma de presentación por definición. Al final, el usuario necesita algo concreto y no una abstracción. Esto podría ser solo cierto en parte para los marcos, pero lo es para las aplicaciones de usuario final.

Sin embargo, la lógica detrás de esto debe estar desacoplada, para que pueda usarla nuevamente con otras tecnologías de presentación. Introduzca interfaces cuando sea necesario para mantener la ortogonalidad de su aplicación. La regla general es: las cosas concretas deben ser intercambiables con otras cosas concretas.

La razón de la pregunta es que siempre me encuentro atascado y colgado sobre cómo generalizar las clases para que sean más útiles. Esto realmente está funcionando en mi contra y me pregunto si estoy "intentando demasiado".

Ya sabes, los programadores extremos son aficionados a su actitud YAGNI . No intentes escribir todo genéricamente y no te esfuerces demasiado tratando de hacer que todo sea de uso general. Esto se llama sobreingeniería y eventualmente conducirá a un código totalmente complicado. Asigne a cada componente exactamente una tarea y asegúrese de que lo haga bien. Ponga abstracciones donde sea necesario, donde espere que las cosas cambien (por ejemplo, interfaz para dibujar el contexto, como se indicó anteriormente).

En general, al escribir aplicaciones comerciales, siempre debe intentar desacoplar las cosas. MVC y MVVM son excelentes para desacoplar la lógica de la presentación, por lo que puede reutilizarla para una presentación web o una aplicación de consola. Tenga en cuenta que al final, algunas cosas tienen que ser concretas. Sus usuarios no pueden trabajar con una abstracción, necesitan algo concreto. Las abstracciones son solo ayudantes para usted, el programador, para mantener el código extensible y mantenible. Debe pensar dónde necesita que su código sea flexible. Finalmente, todas las abstracciones tienen que dar a luz a algo concreto.

Editar: si desea leer más sobre la arquitectura y las técnicas de diseño que pueden proporcionar las mejores prácticas, le sugiero que lea la respuesta de @Catchops y lea sobre las prácticas de SOLID en la wikipedia.

Además, para empezar, siempre recomiendo el siguiente libro: Head First Design Patterns . Le ayudará a comprender las técnicas de abstracción / prácticas de diseño OOP, más que el libro GoF (que es excelente, simplemente no es adecuado para principiantes).

Halcón
fuente
1
Buena respuesta, pero los programadores extremos no "ponen abstracciones donde esperamos que las cosas cambien". Ponemos abstracciones donde las cosas están cambiando, para SECAR el código.
Kevin Cline
1
@kevin cline es increíble, a menos que esté diseñando una biblioteca disponible públicamente con una API que deba ajustarse al comportamiento y las interfaces anteriores.
Magnus Wolffelt
@Magnus: sí, diseñar una biblioteca en algunos idiomas, planificar la compatibilidad binaria hacia atrás, es complicado. Uno se ve obligado a escribir todo tipo de código actualmente innecesario para permitir una extensión futura. Esto puede ser razonable para los idiomas que se compilan en el metal. Es una tontería para los idiomas que se compilan en una máquina virtual.
Kevin Cline
19

Definitivamente tienes razón. Y no, estás "intentando muy bien" :)

Lea sobre el principio de responsabilidad única

Su funcionamiento interno de la clase y la forma en que esta información debe presentarse al usuario son dos responsabilidades.

No tengas miedo de desacoplar las clases. Raramente el problema es demasiada abstracción y desacoplamiento :)

Dos patrones muy relevantes son Model-view-controller para aplicaciones web y Model View ViewModel para Silverlight / WPF.

Boris Yankov
fuente
1
+1 Incluso puedes usar MVC para aplicaciones que no son web, al menos te ayuda a pensar en términos que mantengan claras las responsabilidades.
Patrick Hughes el
MVVM es silimar a MVC. MVC es principalmente para aplicaciones sin estado como aplicaciones web.
Boris Yankov
3
-1 En mi experiencia, uno de los problemas más comunes es la generalización prematura y la sobre ingeniería en general.
dietbuddha
3
Primero debe saber cómo diseñar algo para sobregeniarlo. En mi experiencia, la gente 'sub-ingeniera' software mucho más a menudo.
Boris Yankov
Si bien aprecio los esfuerzos para mejorar el diseño del software, la sustitución de una llamada en una interfaz por una llamada en una clase no desacopla semánticamente nada. Considere lo que sucede cuando se usa un lenguaje de tipo dinámico. Hay demasiado énfasis en el desacoplamiento superficial (sintáctico), y poco en el desacoplamiento verdadero, en los consejos proporcionados en la sección de programadores de intercambio de pila.
Frank Hileman
7

Uso mucho MVVM y, en mi opinión, una clase de objeto comercial nunca debería necesitar saber nada sobre la interfaz de usuario. Claro que es posible que necesiten conocer el SelectedItem, o IsChecked, o IsVisible, etc., pero esos valores no necesitan vincularse a ninguna IU particular y pueden ser propiedades genéricas en la clase.

Si necesita hacer algo a la interfaz en el código subyacente, como configurar Focus, ejecutar una animación, manejar teclas de acceso rápido, etc., el código debe ser parte del código subyacente de la interfaz de usuario, no sus clases de lógica de negocios.

Entonces diría que no dejes de tratar de separar tu interfaz de usuario y tus clases. Cuanto más desacoplados estén, más fáciles serán de mantener y probar.

Rachel
fuente
5

Hay una serie de patrones de diseño probados y verdaderos que se han desarrollado a lo largo de los años para abordar exactamente de lo que está hablando. Otras respuestas a su pregunta se han referido al Principio de responsabilidad única , que es absolutamente válido, y lo que parece estar impulsando su pregunta. Ese principio simplemente establece que una clase necesita hacer una cosa BIEN. En otras palabras, aumentar la Cohesión y reducir el Acoplamiento, que es de lo que se trata un buen diseño orientado a objetos: ¿una clase hace bien una cosa y no depende mucho de otras?

Bueno ... tienes razón al observar que si quieres dibujar un círculo en un iPhone, será diferente a dibujar uno en una PC con Windows. DEBE tener (en este caso) una clase concreta que dibuje uno bien en el iPhone y otra que dibuje uno bien en una PC. Aquí es donde se encuentra la herencia de OO básica que todos esos ejemplos de formas se descomponen. Simplemente no puede hacerlo solo con herencia.

Ahí es donde entran las interfaces, como dice el libro Gang of Four (DEBE LEER), siempre favorezca la implementación sobre la herencia. En otras palabras, use interfaces para armar una arquitectura que pueda realizar varias funciones de muchas maneras sin depender de dependencias codificadas.

He visto referencias a los principios SÓLIDOS . Esos son geniales. La 'S' es el principio de responsabilidad única. PERO, la 'D' significa Inversión de dependencia. El patrón de Inversión de Control (Inyección de Dependencia) puede usarse aquí. Es muy poderoso y puede usarse para responder la pregunta de cómo diseñar un sistema que pueda dibujar un círculo para un iPhone y uno para la PC.

Es posible crear una arquitectura que contenga reglas comerciales comunes y acceso a datos, pero que tenga varias implementaciones de interfaces de usuario utilizando estas construcciones. Sin embargo, realmente ayuda haber estado en un equipo que lo implementó y verlo en acción para comprenderlo realmente.

Esta es solo una respuesta rápida de alto nivel a una pregunta que merece una respuesta más detallada. Te animo a que profundices en estos patrones. Se puede encontrar una implementación más concreta de estos patrones como nombres bien conocidos de MVC y MVVM.

¡Buena suerte!

Catchops
fuente
Me encanta cuando alguien rechaza algo sin dar un comentario de por qué (sarcasmo, por supuesto). Los principios que establecí son ciertos: si el votante negativo se levanta y dice por qué.
Catchops
2

¿Cuál es la mejor práctica cuando se trata de escribir clases que puedan tener que saber sobre la interfaz de usuario?

¿Una clase que sabe dibujar no rompería algunas de las mejores prácticas ya que depende de cuál sea la interfaz de usuario (consola, GUI, etc.)?

En este caso, aún puede usar MVC / MVVM e inyectar diferentes implementaciones de UI usando una interfaz común:

public interface IAgnosticChartDrawing
{
   public void Draw(ChartProperties chartProperties);
   event EventHandler ChartPanned;
}

public class GuiChartDrawer : UserControl, IAgnosticChartDrawing
{
    public void Draw(ChartProperties chartProperties)
    {
        //GDI, GTK or something else...
    }

    //Implement event based on mouse actions
}

public class ConsoleChartDrawer : IAgnosticChartDrawing
{
    public void Draw(ChartProperties chartProperties)
    {
        //'Draw' using characters and symbols...
    }

    //Implement event based on keyboard actions
}

IAgnosticChartDrawing guiView = new GuiChartDrawer();
IAgnosticChartDrawing conView = new ConsoleChartDrawer();

Model model = new FinancialModel();

SampleController controllerGUI = new SampleController(model, guiView);
SampleController controllerConsole = new SampleController(model, conView);

De esta forma, podrá reutilizar la lógica de su Controlador y Modelo mientras puede agregar nuevos tipos de GUI.

Guarida
fuente
2

Hay diferentes patrones para hacer eso: MVP, MVC, MVVM, etc.

Un buen artículo de Martin Fowler (gran nombre) para leer es GUI Architectures: http://www.martinfowler.com/eaaDev/uiArchs.html

MVP aún no se ha mencionado, pero definitivamente merece ser citado: échale un vistazo.

Es el patrón sugerido por los desarrolladores de Google Web Toolkit para usar, es realmente genial.

Puede encontrar código real, ejemplos reales y justificación de por qué este enfoque es útil aquí:

http://code.google.com/webtoolkit/articles/mvp-architecture.html

http://code.google.com/webtoolkit/articles/mvp-architecture-2.html

¡Una de las ventajas de seguir este enfoque u otros similares que no se ha enfatizado lo suficiente aquí es la capacidad de prueba! ¡En muchos casos diría que es la principal ventaja!

Andrea Zilio
fuente
1
+1 para los enlaces. Estaba leyendo un artículo similar en MSDN sobre MVVM y esos artículos de Google son mucho mejores, aunque en un patrón ligeramente diferente.
Pete
1

Este es uno de los lugares donde OOP no puede hacer un buen trabajo en la abstracción. El polimorfismo OOP utiliza el despacho dinámico sobre una sola variable ('esto'). Si el polimorfismo estaba enraizado en Shape, entonces no puede enviar polimórficamente aliado en el renderizador (consola, GUI, etc.).

Considere un sistema de programación que podría distribuir en dos o más variables:

poly_draw(Shape s, Renderer r)

y también suponga que el sistema podría darle una forma de expresar poly_draw para varias combinaciones de tipos de formas y tipos de renderizador. Entonces sería fácil llegar a una clasificación de formas y renderizadores, ¿verdad? El verificador de tipo de alguna manera lo ayudaría a descubrir si había combinaciones de forma y renderizador que quizás no haya implementado.

La mayoría de los lenguajes OOP no admiten nada como el anterior (algunos lo hacen, pero no son convencionales). Te sugiero que eches un vistazo al patrón Visitor para una solución alternativa.

ritesh
fuente
1

... ¿el método draw () no depende mucho de la interfaz de usuario? Si escribimos esta clase, por ejemplo, Win Forms, no podemos reutilizarla para una aplicación de consola o una aplicación web. ¿Es esto correcto?

Arriba me suena correcto. Según tengo entendido, uno puede decir que significa un acoplamiento relativamente estrecho entre el Controlador y la Vista en términos del patrón de diseño MVC. Esto también significa que para cambiar entre desktop-console-webapp, uno tendrá que cambiar tanto Controller como View como un par ; solo el modelo permanece sin cambios.

... Siempre me encuentro atascado y colgado sobre cómo generalizar las clases para que sean más útiles.

Bueno, mi opinión actual anterior es que este acoplamiento de View-Controller del que estamos hablando está bien y aún más, es bastante moderno en diseño moderno.

Sin embargo, hace un año o dos también me sentí inseguro al respecto. He cambiado de opinión después de estudiar discusiones en el foro de Sun sobre patrones y diseño OO.

Si está interesado, pruebe este foro usted mismo: ahora ha migrado a Oracle ( enlace ). Si llegas allí, intenta hacer ping al tipo Saish ; en aquel entonces, sus explicaciones sobre estos asuntos difíciles me resultaron muy útiles. Sin embargo, no puedo decir si todavía participa, yo mismo no he estado allí durante bastante tiempo.

mosquito
fuente
1

¿Pero el método draw () no depende mucho de la interfaz de usuario?

Desde un punto de vista pragmático, algún código en su sistema necesita saber cómo dibujar algo así como Rectanglesi ese es un requisito del usuario final. Y eso se reducirá en algún momento a hacer cosas de muy bajo nivel como rasterizar píxeles o mostrar algo en una consola.

La pregunta para mí desde el punto de vista del acoplamiento es quién / qué debería depender de este tipo de información, y en qué grado de detalle (qué tan abstracto, por ejemplo)

Resumen de capacidades de dibujo / renderizado

Porque si el código de dibujo de nivel superior solo depende de algo muy abstracto, esa abstracción podría funcionar (mediante la sustitución de implementaciones concretas) en todas las plataformas a las que pretende dirigirse. Como ejemplo artificial, alguna IDrawerinterfaz muy abstracta podría ser implementada tanto en la consola como en las API de la GUI para hacer cosas como formas de trazado (la implementación de la consola podría tratar la consola como una "imagen" de 80xN con arte ASCII). Por supuesto, ese es un ejemplo artificial, ya que generalmente eso no es lo que quiere hacer es tratar una salida de consola como un búfer de imagen / marco; por lo general, la mayoría de las necesidades del usuario final requieren más interacciones basadas en texto en las consolas.

Otra consideración es ¿qué tan fácil es diseñar una abstracción estable? Porque podría ser fácil si todo lo que está apuntando son API GUI modernas para abstraer las capacidades básicas de dibujo de formas como trazar líneas, rectángulos, trazados, texto, cosas de este tipo (solo rasterización 2D simple de un conjunto limitado de primitivas) , con una interfaz abstracta que puede implementarse fácilmente para ellos a través de varios subtipos con poco costo. Si puede diseñar tal abstracción de manera efectiva e implementarla en todas las plataformas de destino, entonces diría que es un mal mucho menor, si es que es un mal, para una forma o control de GUI o lo que sea para saber cómo dibujarse usando tal abstracción.

Pero supongamos que está tratando de abstraer los detalles sangrientos que varían entre una Playstation Portable, un iPhone, una XBox One y una poderosa PC para juegos, mientras que sus necesidades son utilizar las técnicas de sombreado / renderizado 3D en tiempo real más avanzadas en cada una . En ese caso, tratar de encontrar una interfaz abstracta para abstraer los detalles de renderizado cuando las capacidades de hardware subyacentes y las API varían enormemente es casi seguro que resultará en un enorme tiempo de diseño y rediseño, una alta probabilidad de cambios recurrentes en el diseño con imprevistos descubrimientos, y del mismo modo una solución de denominador común más bajo que no explota la singularidad y el poder del hardware subyacente.

Hacer que las dependencias fluyan hacia diseños estables y "fáciles"

En mi campo estoy en ese último escenario. Apuntamos a una gran cantidad de hardware diferente con API y capacidades subyacentes radicalmente diferentes, y tratar de encontrar una abstracción de renderizado / dibujo para gobernarlos a todos es casi imposible (podríamos ser mundialmente famosos simplemente haciendo eso de manera efectiva, ya que sería un juego cambiador en la industria). Así que lo último que quiero en mi caso es como el analógico Shapeo Modelo Particle Emitterque sabe cómo dibujar en sí, incluso si se está expresando que el dibujo en el nivel más alto y su forma más abstracta posible ...

... porque esas abstracciones son demasiado difíciles de diseñar correctamente, y cuando un diseño es difícil de corregir, y todo depende de ello, esa es una receta para los cambios de diseño centrales más costosos que ondulan y rompen todo dependiendo de él. Entonces, lo último que desea es que las dependencias en sus sistemas fluyan hacia diseños abstractos demasiado difíciles de corregir (demasiado difíciles de estabilizar sin cambios intrusivos).

Difícil depende de fácil, no fácil depende de difícil

Entonces, lo que hacemos es hacer que las dependencias fluyan hacia cosas que son fáciles de diseñar. Es mucho más fácil diseñar un "Modelo" abstracto que solo se centre en almacenar cosas como polígonos y materiales y obtener ese diseño correcto que diseñar un "Renderer" abstracto que pueda implementarse efectivamente (a través de subtipos de concreto sustituibles) para dar servicio al dibujo solicita uniformemente hardware tan dispares como una PSP desde una PC.

ingrese la descripción de la imagen aquí

Entonces invertimos las dependencias lejos de las cosas que son difíciles de diseñar. En lugar de hacer que los modelos abstractos sepan cómo dibujarse a sí mismos a un diseño de renderizador abstracto del que todos dependen (y romper sus implementaciones si ese diseño cambia), en su lugar tenemos un renderizador abstracto que sabe cómo dibujar cada objeto abstracto en nuestra escena ( modelos, emisores de partículas, etc.), y así podemos implementar un subtipo de renderizador OpenGL para PC como RendererGl, otro para PSP como RendererPsp, otro para teléfonos móviles, etc. En ese caso, las dependencias fluyen hacia diseños estables, fáciles de corregir, desde el renderizador hasta varios tipos de entidades (modelos, partículas, texturas, etc.) en nuestra escena, no al revés.

ingrese la descripción de la imagen aquí

  • Estoy usando "estabilidad / inestabilidad" en un sentido ligeramente diferente de la métrica de acoplamientos aferentes / eferentes del tío Bob, que está midiendo más la dificultad del cambio hasta donde puedo entender. Estoy hablando más sobre la "probabilidad de requerir un cambio", aunque su métrica de estabilidad es útil allí. Cuando la "probabilidad de cambio" es proporcional a la "facilidad de cambio" (por ejemplo: las cosas que más probablemente requieren cambios tienen la mayor inestabilidad y los acoplamientos aferentes de la métrica del tío Bob), entonces cualquier cambio probable es barato y no intrusivo. , que solo requiere reemplazar una implementación sin tocar ningún diseño central.

Si te encuentras tratando de abstraer algo en un nivel central de tu base de código y es demasiado difícil de diseñar, en lugar de golpear obstinadamente las cabezas contra las paredes y hacer cambios intrusivos constantemente cada mes / año que requiere actualizar 8,000 archivos fuente porque es rompiendo todo lo que depende de ello, mi sugerencia número uno es considerar invertir las dependencias. Vea si puede escribir el código de manera tal que lo que es tan difícil de diseñar dependa de todo lo que sea más fácil de diseñar, no tener las cosas que son más fáciles de diseñar dependiendo de lo que es tan difícil de diseñar. Tenga en cuenta que estoy hablando de diseños (específicamente diseños de interfaz) y no implementaciones: a veces las cosas son fáciles de diseñar y difíciles de implementar, y a veces las cosas son difíciles de diseñar pero fáciles de implementar. Las dependencias fluyen hacia los diseños, por lo que el enfoque solo debe centrarse en cuán difícil es diseñar aquí para determinar la dirección en la que fluyen las dependencias.

Principio de responsabilidad única

Para mí, SRP no es tan interesante aquí generalmente (aunque depende del contexto). Quiero decir que hay un acto de equilibrio de la cuerda floja en el diseño de cosas que son claras en su propósito y mantenibles, pero sus Shapeobjetos pueden tener que exponer información más detallada si no saben cómo dibujar, por ejemplo, y puede que no haya muchas cosas significativas para hacer con una forma en un contexto de uso particular que construirla y dibujarla. Hay compensaciones con casi todo, y no está relacionado con SRP que puede hacer que las cosas sean conscientes de cómo dibujarse capaces de convertirse en una pesadilla de mantenimiento en mi experiencia en ciertos contextos.

Tiene mucho más que ver con el acoplamiento y la dirección en la que fluyen las dependencias en su sistema. Si está intentando portar una interfaz de renderización abstracta de la que todo depende (porque la están usando para dibujar) a una nueva API / hardware objetivo y se da cuenta de que tiene que cambiar considerablemente su diseño para que funcione allí efectivamente, entonces Es un cambio muy costoso de realizar que requiere reemplazar las implementaciones de todo lo que sabe dibujar en su sistema. Y ese es el problema de mantenimiento más práctico que encuentro con cosas conscientes de cómo dibujarse si eso se traduce en un montón de dependencias que fluyen hacia abstracciones que son demasiado difíciles de diseñar correctamente por adelantado.

Orgullo desarrollador

Menciono este punto porque, en mi experiencia, este es a menudo el mayor obstáculo para fluir la dirección de las dependencias hacia cosas más fáciles de diseñar. Es muy fácil para los desarrolladores ser un poco ambiciosos aquí y decir: "Voy a diseñar la abstracción de representación multiplataforma para gobernarlos a todos, voy a resolver lo que otros desarrolladores pasan meses en portar, y voy a obtener bien y funcionará como magia en cada plataforma que admitamos y utilizará las técnicas de renderizado más avanzadas en cada una; ya lo imaginé en mi cabeza ".En ese caso, se resisten a la solución práctica que consiste en evitar hacer eso y simplemente cambian la dirección de las dependencias y traducen los cambios de diseño central que pueden ser enormemente costosos y recurrentes en cambios recurrentes simplemente baratos y locales para la implementación. Debe haber algún tipo de instinto de "bandera blanca" en los desarrolladores para darse por vencido cuando algo es demasiado difícil de diseñar a un nivel tan abstracto y reconsiderar toda su estrategia, de lo contrario, se enfrentarán a un gran dolor y pena. Sugeriría transferir tales ambiciones y espíritu de lucha a implementaciones de vanguardia de una cosa más fácil de diseñar que llevar tales ambiciones conquistadoras del mundo al nivel de diseño de interfaz.

Dragon Energy
fuente
1
Parece que no puedo entender la idea de "invertir las dependencias lejos de las cosas que son difíciles de diseñar", ¿estás hablando solo de herencia? Usando el ejemplo de PSP / OpenGL, te refieres a que en lugar de hacer un OpenGLBillboard, ¿harías un OpenGLRendererque sepa dibujar cualquier tipo de IBillBoard? ¿Pero lo haría delegando la lógica a IBillBoard, o tendría grandes interruptores o IBillBoardtipos con condicionales? Eso es lo que me resulta difícil de entender, ¡porque eso no parece sostenible en absoluto!
Steve Chamaillard
@SteveChamaillard La diferencia es, por ejemplo, PSP (hardware limitado) debe manejar primitivas de volumen usando la transparencia de la pantalla de la vieja escuela y un orden diferente de evaluación de renderizado: digitalrune.github.io/DigitalRune-Documentation/media/… . Cuando tienes una central RendererPspque conoce las abstracciones de alto nivel de tu escena de juego, puede hacer toda la magia y los volteretas que necesita para hacer esas cosas de una manera que se vea convincente en la PSP ...
Dragon Energy
Mientras que si tenía tales primitivas de volumen que solicitaban renderizarse, sus operaciones son de tan alto nivel que básicamente se especifican para renderizar (lo que no hace ninguna diferencia excepto la redundancia indirecta y el acoplamiento adicional), o la abstracción del renderizador es realmente incapaz de hacer las cosas de la manera más efectiva posible en la PSP (al menos sin hacer volteretas hacia atrás, lo que no sería necesario si las dependencias se invirtieran).
Dragon Energy
1
Ok, creo que lo tengo ahora. Básicamente, ¿estás diciendo que esas preocupaciones (es decir, el hardware) son de un nivel tan alto que BillBoardsería muy difícil hacer objetos de bajo nivel, como depender de ellos? ¿Mientras que a ya IRendereres de alto nivel y podría depender de estas preocupaciones con muchos menos problemas?
Steve Chamaillard
1
Con las preocupaciones de renderizado de vanguardia en plataformas dispares, capacidades de hardware dispares, es demasiado difícil diseñar algo donde soy el jefe y decirle qué hacer. Es mucho más fácil para mí decir: "Oye, soy una cosa nublada / neblinosa con partículas acuosas grisáceas como esta, y trato de hacerme quedar bien, por favor, no captures mi cuello que no me he afeitado últimamente", y dejar que el renderizador descubra cómo representarme de una manera tan hermosa y en tiempo real como sea posible, dadas las limitaciones con las que está trabajando. Cualquier intento de decirle qué hacer seguramente dará como resultado un camino contraproducente.
Dragon Energy