Acoplamiento de código introducido por DRY y OOD


14

Estoy buscando orientación sobre el acoplamiento DRY vs Code. No me gusta duplicar mi código y tampoco me gusta el acoplamiento de código entre módulos no relacionados. Así que refactorizo ​​el código duplicado si encuentro un código idénticamente duplicado un año después de que se introdujo la duplicación. Sin embargo, he experimentado cada vez más situaciones en las que el mundo real es mucho más impredecible, y después de refactorizar el código, surgen situaciones que requieren volver a descifrar el código.

Por ejemplo, si tuviera un código para manejar autos de gasolina, SUV de gasolina, autos eléctricos y SUV eléctricos, digamos que refactoré el código duplicado en la jerarquía de "gasolina" y en la jerarquía de "electricidad", ambos descendiendo de la jerarquía de "vehículos". Hasta aquí todo bien. Y luego, mi compañía presenta un automóvil híbrido y un Semi híbrido, que requeriría cambios centrales en mi jerarquía original. Tal vez requeriría "composición" entre la gasolina y las jerarquías eléctricas.

Claramente, la duplicación de código es mala porque aumenta el tiempo necesario para implementar un cambio común a todos los productos anteriores. Pero la refactorización del código común hace que sea igualmente difícil introducir variaciones específicas del producto, y conduce a muchos "saltos de clase" cuando uno tiene que encontrar la línea de código para corregir un error: un cambio en una clase principal de nivel superior podría desencadenar errores de regresión desencadenante entre todos los descendientes.

¿Cómo se logra un equilibrio óptimo entre DRY y el acoplamiento de código no deseado?


1
Siempre es bueno no seguir las reglas a ciegas sino involucrar primero al cerebro.
gnasher729

bastante justo, de ahí las pautas, no las reglas.
user2549686

¿Has leído la página Wiki en DRY (u OAOO, como solía llamarse)? wiki.c2.com/?OnceAndOnlyOnce Ahí es donde sucedió gran parte de la discusión inicial al respecto. (En realidad, Ward inventó el Wiki específicamente para discusiones sobre patrones y prácticas.)
Jörg W Mittag

Respuestas muy relacionadas, incluso si la pregunta no suena como un duplicado: softwareengineering.stackexchange.com/questions/300043/…
Hulk

Respuestas:


8

Digamos que refactoré el código duplicado en la jerarquía "gasolina" y en la jerarquía "eléctrica", ambas descendiendo de la jerarquía "vehículo". Hasta aquí todo bien.

Y luego, mi compañía presenta un automóvil híbrido y un Semi híbrido, que requeriría cambios centrales en mi jerarquía original. Tal vez requeriría una "composición" entre la gasolina y las jerarquías eléctricas.

Creo que esta es una de las principales razones por las cuales las personas se están moviendo hacia la composición sobre la herencia.

La herencia lo obliga a una gran reestructuración cuando tiene un cambio conceptual como el que describe.

Cuando la reestructuración es "demasiado grande / difícil", la gente escribe un código duplicado para evitarlo.

En lugar de alejar el duplicado moviendo el código hacia arriba en la cadena de herencia, puede moverlo a una clase o servicio auxiliar y luego inyectar esa clase como parte de una composición cuando sea necesario.

Si el diseño resultante es OOP está abierto a debate


1
+1 por señalar que la herencia es el problema, no SECO. La forma más fácil de reutilizar el código es eliminar dependencias innecesarias, y con demasiada frecuencia las personas pierden el hecho de que una clase primaria (y sus descendientes) son todas dependencias cuando se usa la herencia. Solo cuando reduce o elimina las dependencias, realmente obtiene código reutilizable.
Greg Burghardt

3
" Si el diseño resultante es OOP está abierto a debate ". Todo está abierto a debate. La pregunta que debe hacerse es: ¿está abierto a un debate razonable y racional? La respuesta es no. Creo que su último párrafo resta valor a una muy buena respuesta.
David Arno

@davidarno realmente no te sigue, ¿leí OOD en el título como Diseño orientado a objetos? así que estoy abordando la cuestión sin analizar el debate
Ewan

1
@Ewan: Estoy aquí con David Arno. Creo que es bien sabido por más de 2 décadas que creer "si no hay herencia, entonces no es POO" es una falacia.
Doc Brown

jeeze este es exactamente el tipo de discusión que estaba tratando de evitar. hay puntos de vista en ambos lados, no hay una respuesta definitiva
Ewan

3

Tiene razón, siguiendo el principio DRY, puede aumentar el acoplamiento entre módulos que de otro modo no estarían relacionados. Especialmente en sistemas de software más grandes, esto puede conducir a situaciones en las que no seguir a DRY puede ser la mejor alternativa.

Desafortunadamente, su ejemplo no es adecuado para demostrar esto: los problemas descritos allí son causados ​​por errores clásicos en el uso incorrecto de la herencia por debajo del óptimo. Sin embargo, por lo que he escrito anteriormente, no importa si refactoriza el código común a una clase base común o a una clase auxiliar (composición). En ambos casos, uno podría verse obligado a poner el código común en una biblioteca L y hacer referencia a esa biblioteca desde dos programas anteriormente no relacionados A y B.

Supongamos que A y B no estaban completamente relacionados antes, pueden ser versionados, lanzados y desplegados independientemente. Sin embargo, al poner un código común en una biblioteca L compartida, los nuevos requisitos para A pueden inducir cambios en L, lo que ahora puede causar cambios en B. Por lo tanto, esto provoca la necesidad de pruebas adicionales, y probablemente un nuevo ciclo de lanzamiento e implementación para B.

Entonces, ¿cómo puede manejar esta situación, si no está dispuesto a abandonar el principio DRY? Bueno, hay algunas tácticas bien conocidas para abordar esto:

  1. Mantenga A, B y L como parte del mismo producto, con un número de versión común, un proceso común de construcción, lanzamiento e implementación, con un alto grado de automatización

  2. o haga que L sea un producto por sí solo, con números de versión menores (sin cambios incompatibles) y números de versión principales (que tal vez contengan cambios importantes), y deje que A y B permitan que cada uno haga referencia a una línea de versión diferente de L.

  3. Haga que L sea lo más SÓLIDO posible y cuide la compatibilidad con versiones anteriores. Cuantos más módulos en L se puedan reutilizar sin modificación (OCP), menos probable será que ocurran cambios importantes. Y los otros principios en "SOLID" están ayudando a apoyar ese objetivo.

  4. Use pruebas automáticas especialmente para L, pero también para A y B.

  5. Tenga cuidado con lo que pone en L. La lógica de negocios que debería existir solo en un lugar en un sistema es un buen candidato. Las cosas que simplemente "se parecen" y que pueden variar de manera diferente en el futuro son malos candidatos.

Tenga en cuenta que cuando A y B son desarrollados, mantenidos y evolucionados por equipos diferentes y no relacionados, el principio DRY se vuelve bastante menos importante: DRY se trata de mantenibilidad y capacidad de evolución, pero permitir que dos equipos diferentes proporcionen un esfuerzo de mantenimiento individual a veces puede ser más efectivo que atar sus productos juntos debido a un poco de reutilización.

Entonces, al final, es una compensación. Si desea seguir el principio DRY en sistemas más grandes, debe invertir mucho más esfuerzo en crear componentes robustos y reutilizables, generalmente más de lo que espera. Tienes que confiar en tu juicio cuando vale la pena y cuando no.


1

DRY es otra regla estructural. Eso significa que si lo llevas al extremo es tan malo como si lo ignoras. Aquí hay una metáfora para sacarte de la mentalidad estructural.

Ama a tus gemelos. Mata a los clones.

Cuando copia y pega ciegamente para evitar escribir en el teclado, está creando clones sin pensar. Seguro que hacen lo que quieres, pero te agobian porque cambiar ese comportamiento es muy costoso.

Cuando reproduce un comportamiento idéntico con un código idéntico debido a una responsabilidad diferente, ahora tiene la misma necesidad, pero eso puede someterlo a diferentes cambios, tiene un gemelo que debería ser libre de cambiar y crecer como individuo a medida que pasa el tiempo.

Puede escanear su ADN (código) todo lo que quiera. Los clones y los gemelos son difíciles de distinguir si todo lo que haces es mirarlos. Lo que necesita es comprender el contexto en el que viven. Por qué nacieron. Cuál es su destino final es probable que sea.

Digamos que trabajas para la empresa ABC. Está dirigido por tres ejecutivos de la compañía, A, B y C. Todos ellos quieren que hagas un producto de software, pero cada uno tiene sus propios departamentos. Todos quieren que comiences de a poco. Simplemente envíe un mensaje simple por ahora. Entonces escribes "Hola Mundo".

A lo odia, quiere que pongas el nombre de la empresa allí.

A B le encanta, quiere que lo deje en paz y agregue el nombre de la empresa a una pantalla de inicio.

C solo quiere una calculadora y pensó que el mensaje era solo una forma de comenzar.

De una cosa está seguro, estos muchachos tienen ideas muy diferentes de lo que trata este proyecto. A veces van a estar de acuerdo. A veces te empujarán en diferentes direcciones.

Cuando duplica el código porque está creando la capacidad de que ese código varíe independientemente, está creando un gemelo. Cuando duplica el código porque escribir es complicado y copiar y pegar es fácil, está creando clones malvados.

A, B y C son maestros diferentes a los que debe servir su código. Ninguna línea de código puede servir a más de un maestro por mucho tiempo.


En primer lugar, muchas gracias a todos por todos los comentarios.
user2549686
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.