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?
design
design-patterns
architecture
object-oriented-design
design-principles
SteveCallender
fuente
fuente
Respuestas:
Piense en sus paquetes como una API, para tomar el ejemplo del documento, tome definiciones
Reader
constring Reader.Read()
yWriter
convoid Writer.Write(string)
su API abstracta.Luego puede crear una clase
Copy
con un métodoCopier.Copy(Reader, Writer)
y la implementaciónWriter.Write(Reader.Read())
y tal vez algunas comprobaciones de cordura.Ahora, se hace implementaciones concretas, por ejemplo
FileReader
,FileWriter
,KeyboardReader
yDownloadThingsFromTheInternetReader
.¿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 cambiarCopier
,FileReader
,KeyboardReader
yDownloadThingsFromTheInternetReader
.Esta es la razón detrás del Principio de abstracción estable: haga que sus concreciones sean menos estables que las abstracciones.
fuente
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).
fuente
Creo que quizás te confunde la palabra estable elegida por Robert Martin. Aquí es donde creo que comienza la confusión:
Si lees el artículo original , verás (el énfasis es mío):
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 ):
Entonces, conectando estas definiciones en la declaración
Citemos el Principio de abstracciones estables (SAP), enfatizando las palabras confusas estable / inestable:
Aclarándolo sin estas palabras confusas:
TL; DR
El título de su pregunta pregunta:
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.
fuente
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á? ;)
fuente
Tenga en cuenta la métrica de estabilidad de Martin y lo que quiere decir con "estabilidad":
O:
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.
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 (MotionComponent
por ejemplo,) que utilizan todos los sistemas.fuente