Duplicación de código ilusorio

54

El instinto habitual es eliminar cualquier duplicación de código que vea en el código. Sin embargo, me encontré en una situación en la que la duplicación es ilusoria .

Para describir la situación con más detalles: estoy desarrollando una aplicación web, y la mayoría de las vistas son básicamente las mismas: muestran una lista de elementos que el usuario puede desplazar y elegir, una segunda lista que contiene elementos seleccionados y un botón "Guardar" para guardar la nueva lista.

Me pareció que el problema es fácil. Sin embargo, cada vista tiene sus propias peculiaridades: a veces necesitas volver a calcular algo, a veces debes almacenar algunos datos adicionales, etc. Estos, los resolví insertando enlaces de devolución de llamada en el código lógico principal.

Hay tantas diferencias minúsculas entre las vistas que cada vez son menos fáciles de mantener, porque necesito proporcionar devoluciones de llamada para prácticamente toda la funcionalidad, y la lógica principal comienza a parecer una gran secuencia. de invocaciones de devolución de llamada. Al final no estoy guardando ningún tiempo o código, porque cada vista tiene su propio código que se ejecuta, todo en devoluciones de llamada.

Los problemas son:

  • las diferencias son tan pequeñas que el código se ve casi exactamente igual en todas las vistas,
  • hay tantas diferencias que al mirar los detalles, el código no se parece en nada

¿Cómo debo manejar esta situación?
¿Tener una lógica central compuesta completamente de llamadas de devolución de llamada es una buena solución?
¿O debería duplicar el código y eliminar la complejidad del código basado en la devolución de llamada?

    
pregunta Mael 16.10.2015 - 13:15

6 respuestas

52

En última instancia, debes hacer una llamada de juicio acerca de si se debe combinar un código similar para eliminar la duplicación.

Parece que hay una desafortunada tendencia a tomar principios como "No te repitas" como reglas que deben seguirse de manera permanente. De hecho, estas no son reglas universales, sino pautas que deberían ayudarlo a pensar y desarrollar un buen diseño.

Como todo en la vida, debe considerar los beneficios frente a los costos. ¿Cuánto código duplicado se eliminará? ¿Cuántas veces se repite el código? ¿Cuánto esfuerzo costará escribir un diseño más genérico? ¿Cuánto es probable que desarrolles el código en el futuro? Y así sucesivamente.

Sin saber su código específico, esto no está claro. Quizás haya una forma más elegante de eliminar la duplicación (como la sugerida por LindaJeanne). O tal vez simplemente no hay suficiente repetición verdadera para justificar la abstracción.

La atención insuficiente al diseño es un escollo, pero también tenga cuidado con el diseño excesivo.

    
respondido por el user82096 16.10.2015 - 13:57
42

Recuerde que DRY tiene que ver con el conocimiento . No importa si dos piezas de código parecen similares, idénticas o totalmente diferentes, lo que importa es si la misma pieza de conocimiento sobre su sistema se puede encontrar en ambas.

Un conocimiento puede ser un hecho ("la desviación máxima permitida del valor deseado es 0.1%") o podría ser algún aspecto de su proceso ("esta cola nunca contiene más de tres elementos"). Es esencialmente cualquier pieza de información codificada en su código fuente.

Entonces, cuando esté decidiendo si algo es duplicación que debería eliminarse, pregunte si es duplicación de conocimiento. Si no, es probable que sea una duplicación accidental, y extraerlo en algún lugar común causará problemas cuando más tarde quiera crear un componente similar donde esa parte aparentemente duplicada sea diferente.

    
respondido por el Ben Aaronson 16.10.2015 - 17:50
27

¿Ha considerado utilizar un patrón de estrategia ? Tendría una clase de Vista que contiene el código común y amp; Rutinas llamadas por varias vistas. Los hijos de la clase de Vista contendrían el código específico para esas instancias. Todos usarían la interfaz común que creó para la Vista y, por lo tanto, las diferencias se encapsularían & coherente.

    
respondido por el LindaJeanne 16.10.2015 - 13:41
5

¿Cuál es el potencial de cambio? Por ejemplo, nuestra aplicación tiene 8 áreas comerciales diferentes con un potencial de 4 o más tipos de usuarios para cada área. Las vistas se personalizan según el tipo de usuario y el área.

Inicialmente, esto se hizo usando la misma vista con algunas comprobaciones aquí y allá para determinar si deberían mostrarse cosas diferentes. Con el tiempo, algunas de las áreas de negocios han decidido hacer cosas drásticamente diferentes. Al final, básicamente migramos a una vista (vistas parciales, en el caso de ASP.NET MVC) por pieza de funcionalidad por área de negocio. No todas las áreas de negocios tienen la misma funcionalidad, pero si una quiere la funcionalidad que tiene otra, esa área tiene su propia vista. Es mucho menos engorroso para la comprensión del código, así como para la capacidad de prueba. Por ejemplo, hacer un cambio en un área no causará un cambio no deseado en otra área.

Como se mencionó en @ dan1111, puede ser una decisión de juicio. Con el tiempo, puede descubrir si funciona o no.

    
respondido por el ps2goat 17.10.2015 - 00:14
2

Un problema puede ser que esté proporcionando una interfaz (interfaz teórica, no función de idioma) a un solo nivel de la funcionalidad:

A(a,b,c) //a,b,c are your callbacks or other dependencies

En lugar de múltiples niveles dependiendo de cuánto control se requiere:

//high level
A(a,b,c)
//lower
A myA(a,b)
B(myA,c)
//even lower
A myA(a)
B myB(myA,b)
C myC(myB,c)
//all the way down to you just having to write the code yourself

Por lo que entendí, solo expones la interfaz de alto nivel (A), ocultando los detalles de la implementación (las otras cosas allí).

La ocultación de los detalles de la implementación tiene ventajas, y acaba de encontrar una desventaja: el control es limitado, a menos que agregue explícitamente funciones para cada cosa que hubiera sido posible al usar directamente la (s) interfaz (es) de bajo nivel.

Entonces, tienes dos opciones. O solo utiliza la interfaz de bajo nivel, use la interfaz de bajo nivel porque la interfaz de alto nivel era demasiado trabajo para mantener, o expone las interfaces de alto y bajo nivel. La única opción sensata es ofrecer interfaces de alto y bajo nivel (y todo lo que se encuentre entre ellas), asumiendo que desea evitar el código redundante.

Luego, al escribir otra de tus cosas, miras todas las funciones disponibles que has escrito hasta ahora (innumerables posibilidades, tú decides las que puedes reutilizar) y las unes.

Use un solo objeto donde necesite poco control.

Use la funcionalidad de nivel más bajo cuando sea necesario que ocurran algunas rarezas.

Tampoco es muy blanco y negro. Tal vez su clase de alto nivel pueda cubrir razonablemente todos los casos de uso posibles. Tal vez los casos de uso son tan variados que no basta con nada más que la funcionalidad primitiva de nivel más bajo. Depende de usted para encontrar el equilibrio.

    
respondido por el Waterlimon 17.10.2015 - 22:44
1

Ya hay otras respuestas útiles. Añadiré el mío.

La duplicación es mala porque

  1. desordena el código
  2. desordena nuestra comprensión del código pero lo más importante
  3. porque si cambias algo aquí y tú también tienes que cambiar algo allí , podrías olvidar / introducir errores / .... y es difícil nunca olvidarlos.

Entonces el punto es: no estás eliminando la duplicación por eso o porque alguien dijo que es importante. Lo estás haciendo porque quieres reducir errores / problemas. En su caso, parece que si cambia algo en una vista, es probable que no necesite cambiar exactamente la misma línea en todas las demás vistas. Por lo tanto, tiene duplicación aparente , no duplicación real.

Otro punto importante es nunca reescribir desde cero algo que funciona ahora solo sobre la base de un asunto de principio, como dijo Joel (ya podrías haber oído hablar de él ...). Por lo tanto, si sus puntos de vista están funcionando, proceda a mejorar paso a paso y no caiga en el "peor error estratégico que cualquier empresa de software puede hacer".

    
respondido por el Francesco 20.10.2015 - 14:40

Lea otras preguntas en las etiquetas