¿Existen desventajas significativas al depender de abstracciones?

9

Estaba leyendo este wiki sobre el Principio de abstracciones estables (SAP) .

El SAP afirma que cuanto más estable sea un paquete, más abstracto debería ser. Esto implica que si un paquete es menos estable (es más probable que cambie), entonces debería ser más concreto. Lo que realmente no entiendo es por qué este debería ser el caso. ¿Seguramente en todos los casos, independientemente de la estabilidad, deberíamos depender de abstracciones y ocultar la implementación concreta?

SteveCallender
fuente
Intente utilizar un componente con el que se sienta cómodo, no utilizando las abstracciones que proporciona, sino haciendo todo con todo detalle en un nivel inferior al que está acostumbrado. Eso le dará una buena impresión de las ventajas y desventajas de la abstracción.
Kilian Foth
2
¿Leyó el artículo vinculado y / o el libro en el que se basa el artículo?
Jörg W Mittag
1
+1 Buena pregunta, especialmente porque no creo que la relación entre estabilidad y abstracción sea inmediatamente intuitiva. La página 11 de este artículo ayuda, su ejemplo del caso abstracto tiene sentido, pero quizás alguien pueda escribir un ejemplo más claro del caso concreto. Solicitud despegando.
Mike
¿Con qué dominio de problemas estás lidiando con estas abstracciones? Como se señaló en C2: "Al modelar dominios del mundo real (el mundo de los clientes, empleados, facturas, listas de materiales, productos, SKU, cheques de pago, etc.), las abstracciones estables pueden ser difíciles de encontrar. Dominios computacionales: el mundo de pilas, colas, funciones, árboles, procesos, hilos, widgets gráficos, informes, formularios, etc., es mucho más probable que sean estables ". y "En algunos dominios, es difícil encontrar abstracciones estables". Sin saber qué problema está tratando de resolver con SAP, es difícil darle una buena respuesta.
@ JörgWMittag y Mike - Sí, leí el artículo. Simplemente siento que falta una explicación de por qué "los paquetes inestables deben ser concretos". En la página 13 de dicho artículo, muestra un gráfico, pero en realidad no continúa explicando con demasiado detalle ¿por qué se debe evitar (1,1) en el gráfico? ¿Es la idea que básicamente inestable significa dependencias menos aferentes y que no hay necesidad de usar la abstracción? Si es así ... ¿no es una buena práctica usar la abstracción de todos modos, por si acaso los cambios de estabilidad con cambios en los requisitos ..
SteveCallender

Respuestas:

7

Piense en sus paquetes como una API, para tomar el ejemplo del documento, tome definiciones Readercon string Reader.Read()y Writercon void Writer.Write(string)su API abstracta.

Luego puede crear una clase Copycon un método Copier.Copy(Reader, Writer)y la implementación Writer.Write(Reader.Read())y tal vez algunas comprobaciones de cordura.

Ahora, se hace implementaciones concretas, por ejemplo FileReader, FileWriter, KeyboardReadery DownloadThingsFromTheInternetReader.

¿Qué pasa si desea cambiar su implementación de FileReader? No hay problema, solo cambia la clase y recompila.

¿Qué pasa si quieres cambiar la definición de tu abstracción Reader? Vaya, que no sólo puede cambiar eso, sino que también tendrá que cambiar Copier, FileReader, KeyboardReadery DownloadThingsFromTheInternetReader.

Esta es la razón detrás del Principio de abstracción estable: haga que sus concreciones sean menos estables que las abstracciones.

Residuo
fuente
1
Estoy de acuerdo con todo lo que dices, pero creo que la definición de estabilidad de los autores y la tuya difieren. Usted trata la estabilidad como si fuera necesario cambiar, dice el autor "la estabilidad no es una medida de la probabilidad de que un módulo cambie, sino que es una medida de la dificultad para cambiar un módulo". Entonces, mi pregunta es más ¿por qué es beneficioso que los paquetes que se cambian fácilmente sean más concretos que abstractos?
SteveCallender
1
@SteveCallender Es una sutil diferencia: la definición del autor de "estabilidad" es lo que la mayoría de la gente llama "necesidad de estabilidad", es decir, cuanto más módulos dependen de un módulo, más "estable" debe ser un módulo.
Residuo
6

Por YAGNI .

Si actualmente solo tiene una implementación de una cosa , ¿por qué molestarse con una capa adicional e inútil? Solo conducirá a una complejidad innecesaria. Peor aún, a veces proporciona una abstracción pensando en el día en que vendrá una segunda implementación ... y este día nunca sucede. ¡Qué desperdicio de trabajo!

También creo que la verdadera pregunta que debe hacerse no es "¿Debo depender de las abstracciones?" sino más bien "¿Necesito modularidad?". Y la modularidad no siempre es necesaria, ver más abajo.

En la empresa en la que estoy trabajando, algunos de los softwares que desarrollo están fuertemente vinculados a algún dispositivo de hardware con el que deben comunicarse. Estos dispositivos están desarrollados para cumplir objetivos muy específicos y son todo menos modulares. :-) Una vez que el primer dispositivo producido sale de la fábrica y se instala en algún lugar, tanto en su firmware y hardware nunca puede cambiar, jamás .

Por lo tanto, puedo estar seguro de que algunas partes del software nunca evolucionarán. Estas partes no necesitan depender de abstracciones ya que solo existe una implementación y esta nunca cambiará. Declarar abstracciones en estas partes del código solo confundirá a todos y tomará más tiempo (sin producir ningún valor).

Manchado
fuente
1
Tiendo a estar de acuerdo con YAGNI, pero me pregunto cuál es tu ejemplo. ¿Nunca repites ningún código en los diferentes dispositivos? Me resulta difícil creer que no haya algún código común en los dispositivos de la misma compañía. Además, ¿qué les gusta a los clientes cuando no corrigen errores en su firmware? ¿Estás diciendo que nunca hay errores, nunca ? Si tiene el mismo código que tiene errores en 4 implementaciones diferentes, debe corregir el error 4 veces si no está en un módulo común.
Fuhrmanator
1
El código común de @Fuhrmanator es diferente de las abstracciones. El código común solo puede significar un método o biblioteca auxiliar: no se necesitan abstracciones.
Eilon
@Fuhrmanator Por supuesto, tenemos un código común en las bibliotecas, pero, como dijo Eilon, no todo depende de las abstracciones (algunas partes sí). Nunca dije que nunca hay errores, dije que no se pueden reparar (por razones que están fuera del alcance de la pregunta del OP).
Visto el
@Eilon mi comentario fue sobre la modularidad no siempre es necesaria (no abstracciones).
Fuhrmanator
@Spotted No hay problema por no poder parchear. Es solo un ejemplo bastante específico y no típico de la mayoría del software.
Fuhrmanator
6

Creo que quizás te confunde la palabra estable elegida por Robert Martin. Aquí es donde creo que comienza la confusión:

Esto implica que si un paquete es menos estable (es más probable que cambie), entonces debería ser más concreto.

Si lees el artículo original , verás (el énfasis es mío):

La definición clásica de la palabra estabilidad es: "No se mueve fácilmente". Esta es la definición que usaremos en este artículo. Es decir, la estabilidad no es una medida de la probabilidad de que un módulo cambie; más bien es una medida de la dificultad para cambiar un módulo .

Claramente, los módulos que son más difíciles de cambiar serán menos volátiles. Cuanto más difícil sea cambiar el módulo, es decir, cuanto más estable sea, menos volátil será.

Siempre he luchado con la elección del autor de la palabra estable , ya que (como usted) tiendo a pensar en el aspecto de "probabilidad" de la estabilidad, es decir, es poco probable que cambie . La dificultad implica que cambiar ese módulo romperá muchos otros módulos, y va a ser mucho trabajo arreglar el código.

Martin también usa las palabras independiente y responsable , que para mí transmiten mucho más significado. En su seminario de capacitación, utilizó una metáfora sobre los padres de niños que crecen y cómo deben ser "responsables", porque sus hijos dependen de ellos. El divorcio, el desempleo, el encarcelamiento, etc. son excelentes ejemplos del mundo real del impacto negativo que el cambio en los padres tendrá en los hijos. Por lo tanto, los padres deben ser "estables" en beneficio de sus hijos. Por cierto, esta metáfora de hijos / padres no está necesariamente relacionada con la herencia en OOP!

Entonces, siguiendo el espíritu de "responsable", se me ocurrieron significados alternativos para difíciles de cambiar (o no deberían cambiar ):

  • Obligado: lo que significa que otras clases dependen de esta clase, por lo que no debería cambiar.
  • Beholden - ibid.
  • Restringido: las obligaciones de esta clase limitan su facilidad de cambio.

Entonces, conectando estas definiciones en la declaración

cuanto más estable sea un paquete, más abstracto debería ser

  • cuanto más obligado sea un paquete, más abstracto debería ser
  • cuanto más en deuda esté un paquete, más abstracto debería ser
  • cuanto más limitado sea un paquete, más abstracto debería ser

Citemos el Principio de abstracciones estables (SAP), enfatizando las palabras confusas estable / inestable:

Los paquetes que son máximamente estables deben ser máximamente abstractos. Los paquetes inestables deben ser concretos. La abstracción de un paquete debe ser proporcional a su estabilidad .

Aclarándolo sin estas palabras confusas:

Los paquetes que están en deuda máxima con otras partes del sistema deben ser máximamente abstractos. Los paquetes que pueden cambiar sin dificultad deben ser concretos. La abstracción de un paquete debe ser proporcional a lo difícil que será modificarlo .

TL; DR

El título de su pregunta pregunta:

¿Existen desventajas significativas al depender de abstracciones?

Creo que si crea las abstracciones correctamente (p. Ej., Existen porque una gran cantidad de código depende de ellas), entonces no hay desventajas significativas.

Fuhrmanator
fuente
0

Esto implica que si un paquete es menos estable (es más probable que cambie), entonces debería ser más concreto. Lo que realmente no entiendo es por qué este debería ser el caso.

Las abstracciones son cosas que son difíciles de cambiar en el software porque todo depende de ellas. Si su paquete va a cambiar con frecuencia y proporciona abstracciones, las personas que dependen de él se verán obligadas a reescribir una gran cantidad de su código cuando cambie algo. Pero si su paquete inestable proporciona algunas implementaciones concretas, será necesario reescribir mucho menos código después de los cambios.

Entonces, si su paquete va a cambiar con frecuencia, debería proporcionar concretos, no abstracciones. De lo contrario ... ¿quién demonios lo usará? ;)

Vladislav Rastrusny
fuente
0

Tenga en cuenta la métrica de estabilidad de Martin y lo que quiere decir con "estabilidad":

Instability = Ce / (Ca+Ce)

O:

Instability = Outgoing / (Incoming+Outgoing)

Es decir, un paquete se considera completamente inestable si todas sus dependencias son salientes: usa otras cosas, pero nada lo usa. En ese caso, solo tiene sentido que esa cosa sea concreta. También será el tipo de código más fácil de cambiar ya que nada más lo usa y, por lo tanto, nada más puede romperse si ese código se modifica.

Mientras tanto, cuando tienes el escenario opuesto de "estabilidad" completa con un paquete usado por una o más cosas pero no usa nada por sí solo, como un paquete central usado por el software, es cuando Martin dice que esto debería ser resumen. Eso también se ve reforzado por la parte DIP de SOLI (D), el Principio de Inversión de Dependencia, que básicamente establece que las dependencias deben fluir uniformemente hacia las abstracciones para el código de bajo y alto nivel.

Es decir, las dependencias deberían fluir uniformemente hacia la "estabilidad", y más precisamente, las dependencias deberían fluir hacia paquetes con más dependencias entrantes que las dependencias salientes y, además, las dependencias deberían fluir hacia abstracciones. La esencia de la razón detrás de eso es que las abstracciones proporcionan espacio para respirar para sustituir un subtipo por otro, ofreciendo ese grado de flexibilidad para que las partes concretas que implementan la interfaz cambien sin romper las dependencias entrantes a esa interfaz abstracta.

¿Existen desventajas significativas al depender de abstracciones?

Bueno, en realidad no estoy de acuerdo con Martin aquí para mi dominio al menos, y aquí necesito introducir una nueva definición de "estabilidad" como en "falta de razones para cambiar". En ese caso, diría que las dependencias deberían fluir hacia la estabilidad, pero las interfaces abstractas no ayudan si las interfaces abstractas son inestables (según mi definición de "inestable", ya que son propensas a ser cambiadas repetidamente, no las de Martin). Si los desarrolladores no pueden corregir las abstracciones y los clientes cambian de opinión repetidamente de manera que los intentos abstractos de modelar el software sean incompletos o ineficaces, entonces ya no nos beneficiamos de la flexibilidad mejorada de las interfaces abstractas para proteger el sistema contra cambios en cascada que rompen la dependencia . En mi caso personal, he encontrado motores ECS, como los que se encuentran en los juegos AAA,lo más concreto : hacia datos en bruto, pero dichos datos son altamente estables (como en "es improbable que sea necesario cambiar"). A menudo he encontrado que la probabilidad de que algo que requiera cambios futuros sea una métrica más útil que la relación de acoplamientos eferentes a totales en las decisiones orientadoras de SE.

Así que alteraría un poco el DIP y solo diría, "las dependencias deberían fluir hacia los componentes que tienen la menor probabilidad de requerir más cambios", independientemente de si esos componentes son interfaces abstractas o datos sin procesar. Lo único que me importa es la probabilidad de que puedan requerir cambios directos que rompan el diseño. Las abstracciones solo son útiles en este contexto de estabilidad si algo, al ser abstracto, reduce esa probabilidad.

Para muchos contextos, ese podría ser el caso de ingenieros y clientes decentes que anticipan las necesidades del software por adelantado y diseñan abstracciones estables (como inmutables), mientras que esas abstracciones les ofrecen todo el respiro que necesitan para intercambiar implementaciones concretas. Pero en algunos dominios, las abstracciones pueden ser inestables y propensas a ser inadecuadas, mientras que los datos requeridos por el motor pueden ser mucho más fáciles de anticipar y estabilizar por adelantado. Entonces, en esos casos, en realidad puede ser más beneficioso desde el punto de vista de la mantenibilidad (la facilidad de cambiar y extender el sistema) para que las dependencias fluyan hacia los datos en lugar de las abstracciones. En un ECS, las partes más inestables (como en las partes que se cambian con mayor frecuencia) son típicamente la funcionalidad que reside en los sistemas (PhysicsSystem, por ejemplo), mientras que las partes más estables (como las menos susceptibles de ser cambiadas) son los componentes que solo consisten en datos sin procesar ( MotionComponentpor ejemplo,) que utilizan todos los sistemas.


fuente