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 donde 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 desplazarse y elegir, una segunda lista que contiene elementos seleccionados y un "Guardar "para guardar la nueva lista.
Me pareció que el problema es fácil. Sin embargo, cada vista tiene sus propias peculiaridades: a veces es necesario volver a calcular algo, a veces debe almacenar algunos datos adicionales, etc. Esto lo resolví insertando ganchos de devolución de llamada en el código lógico principal.
Hay tantas diferencias mínimas entre las vistas que cada vez es menos mantenible, porque necesito proporcionar devoluciones de llamada para básicamente todas las funciones, y la lógica principal comienza a parecerse a una gran secuencia de invocaciones de devolución de llamada. Al final, no estoy ahorrando tiempo ni 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 cuando miras los detalles, codificar no es un poco similar
¿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 preferir duplicar el código y descartar la complejidad del código basado en la devolución de llamada?
fuente
Respuestas:
En última instancia, debe tomar una decisión sobre si combinar código similar para eliminar la duplicación.
Parece haber una tendencia desafortunada a tomar principios como "No te repitas" como reglas que deben seguirse de memoria en todo momento. 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 será escribir un diseño más genérico? ¿Cuánto es probable que desarrolle el código en el futuro? Y así.
Sin conocer 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 garantizar la abstracción.
La atención insuficiente al diseño es una trampa, pero también tenga cuidado con el diseño excesivo.
fuente
Recuerda que DRY se trata de conocimiento . No importa si dos partes del código se ven similares, idénticas o totalmente diferentes, lo que importa es si se puede encontrar la misma información sobre su sistema 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 decida si algo es duplicación que debe eliminarse, pregunte si es duplicación de conocimiento. De lo contrario, es probable que sea una duplicación incidental, y extraerlo a un lugar común causará problemas cuando luego desee crear un componente similar donde esa parte aparentemente duplicada sea diferente.
fuente
¿Has considerado usar un patrón de estrategia ? Tendría una clase de Vista que contiene el código común y las rutinas llamadas por varias vistas. Los hijos de la clase View contendrían el código específico de esas instancias. Todos usarían la interfaz común que creó para la Vista y, por lo tanto, las diferencias serían encapsuladas y coherentes.
fuente
¿Cuál es el potencial de cambio? Por ejemplo, nuestra aplicación tiene 8 áreas de negocios 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 diferentes cosas. 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 uno 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 para un área no causará un cambio no deseado para otra área.
Como @ dan1111 mencionó, puede ser una decisión judicial. Con el tiempo, puede descubrir si funciona o no.
fuente
Un problema podría ser que está proporcionando una interfaz (interfaz teórica, no una función de lenguaje) a un solo nivel de la funcionalidad:
En lugar de múltiples niveles dependiendo de cuánto control se requiera:
Según tengo entendido, solo expone la interfaz de alto nivel (A), ocultando los detalles de implementación (las otras cosas allí).
Ocultar los detalles de la implementación tiene ventajas, y usted acaba de encontrar una desventaja: el control es limitado, a menos que agregue explícitamente características para cada cosa que hubiera sido posible al usar directamente las interfaces de bajo nivel.
Así que tienes dos opciones. O solo usa 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 demás), suponiendo que desea evitar el código redundante.
Luego, al escribir otra de sus cosas, observa todas las funcionalidades disponibles que ha escrito hasta ahora (innumerables posibilidades, hasta que usted decida cuáles podrían reutilizarse) y las junta.
Use un solo objeto donde necesite poco control.
Utilice la funcionalidad de nivel más bajo cuando deba ocurrir alguna rareza.
Tampoco es muy blanco y negro. Tal vez su gran clase de alto nivel PUEDE cubrir razonablemente todos los casos de uso posibles. Tal vez los casos de uso varían tanto que no basta con la funcionalidad primitiva de nivel más bajo. Depende de usted para encontrar el equilibrio.
fuente
Ya hay otras respuestas útiles. Agregaré el mío.
La duplicación es mala porque
Entonces, el punto es: no estás eliminando la duplicación por el bien o porque alguien dijo que era importante. Lo estás haciendo porque quieres reducir errores / problemas. En su caso, parece que si cambia algo en una vista, probablemente no necesitará cambiar exactamente la misma línea en todas las otras vistas. Entonces tiene una duplicación aparente , no una duplicación real.
Otro punto importante es no volver a escribir desde cero algo que funciona ahora solo por una cuestión de principios, como dijo Joel (ya podría haber oído hablar de él ...). Entonces, 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 compañía de software puede cometer".
fuente