Muy simple, ¿por qué querría escribir código que funcione para todos los casos y datos escalables cuando todo lo que necesito hacer es repetir el mismo proceso varias veces con algunos pequeños ajustes?
Es poco probable que necesite editar esto nuevamente en el corto plazo.
Parece mucho menos trabajo simplemente ir ...
function doStuff1(){/*.a.*/}
function doStuff2(){/*.b.*/}
function doStuff3(){/*.c.*/}
Y si alguna vez necesito agregar algo ...
function doStuff4(){/*.d.*/}
Y si necesito eliminarlo, lo elimino.
Es más difícil descubrir cómo hacer que todo eso se convierta en un patrón directo en el que pueda alimentar datos y tratar todos los casos, y hacer un montón de cambios que no creo que vaya a tener. que hacer.
¿Por qué estar SECO cuando parece que un corte rápido + pegar va a ser mucho menos trabajo?
code-quality
dry
Incógnito
fuente
fuente
Respuestas:
Si se repite, puede crear problemas de mantenimiento. Si doStuff1-3 tiene un código estructurado de manera similar y soluciona un problema en uno, podría olvidarse fácilmente de solucionar el problema en otros lugares. Además, si tiene que agregar un nuevo caso para manejar, simplemente puede pasar diferentes parámetros a una función en lugar de copiar y pegar en todo el lugar.
Sin embargo, DRY a menudo es llevado al extremo por programadores inteligentes. A veces, para no repetirse, debe crear abstracciones tan obtusas que sus compañeros de equipo no puedan seguirlas. A veces, la estructura de dos cosas es solo vagamente similar pero lo suficientemente diferente. Si doStuff1-4 es lo suficientemente diferente como para que refactorizarlos para que no se repitan, tendrás que escribir un código no natural o sufrir backflips de codificación inteligentes que harán que tu equipo te fulmine con la mirada, entonces puede estar bien repetirlo. Me he esforzado por no repetirme un par de veces de forma antinatural y lamenté el producto final.
Siempre me equivoco al lado de DRY, en el raro caso de repetirme cuando pienso que los beneficios de la legibilidad valen los riesgos de que alguien se olvide de corregir un error en varios lugares.
Teniendo en cuenta ese consejo, suena como en tu caso
Definitivamente trabajaría duro para no repetirme en su caso. Suponiendo "ajustes" mínimos: se pueden manejar con diferentes parámetros que afectan el comportamiento o tal vez se inyectan dependencia para realizar diferentes subtareas.
Últimas palabras famosas. Te arrepentirás de pensar que cuando un ingeniero junior modifica / repara / refactoriza un doStuff y ni siquiera se da cuenta de que el otro existe. Produce hilaridad. No se produce principalmente acidez estomacal. Cada línea de código cuesta más. ¿Cuántas rutas de código debe probar con tantas funciones repetidas? Si una función, solo tiene que probar una ruta principal con algunas modificaciones de comportamiento. Si está pegado, debe probar cada doStuff por separado. Lo más probable es que te pierdas uno y un cliente puede tener un error no deseado y es posible que tengas algunos correos electrónicos no deseados en tu bandeja de entrada.
fuente
Porque SECO será menos trabajo más tarde.
SECO: (No te repitas)
Una función tomando un argumento.
C&P: (copiar y pegar)
26 millones de funciones hacen esencialmente lo mismo, pero con una diferencia de 2 caracteres.
¿Qué tal si actualizamos nuestra impresión para especificar qué es exactamente la impresión?
SECO:
Hecho.
C&P:
Tienes que regresar y cambiar cada función .
¿Cuál crees que sería más fácil de depurar?
fuente
Porque , aplicado a tu ejemplo:
+ legibilidad
Menos código a menudo se traduce en menos ruido . (no siempre...)
+ flexibilidad
Si alguna vez tuvo que cambiar el comportamiento de la
doStuffX
, querrá suicidarse o quien lo escribió,+ extensibilidad
Si ha extraído las distintas partes de una estructura de datos de su elección y luego solo ha iterado sobre ella llamando a un genérico
doStuff
, también podría agregar una línea en su estructura de datos donde desea una nueva entrada, o eliminar una, y cambiar el comportamiento solo significará editardoStuff
. Más fácil de mantener .+ rentabilidad
menos código aquí significa:
+ (posible) optimización gestionada
Dependiendo del idioma, el compilador / intérprete podría tener una mayor probabilidad de determinar que el genérico
doStuff
hace siempre las cosas casi idénticas, a menudo una llamada tras otra, y podría alinearlo o intentar optimizarlo . Probablemente no para X variaciones dedoStuffX
.+ pruebas y calidad
La prueba es más fácil:
doStuff
necesita pruebas, y eso es todo. Bueno, no exactamente, pero eso ya cubre más . Solo sus expectativas de IO varían y deben probarse en diferentes condiciones, pero aún así es mucho más fácil de probar y más sostenible que todas las variaciones dedoStuffX
.En general, esto representa un código más fácil de mantener y una eficiencia de desarrollo mejorada para su equipo, y es una de las muchas buenas prácticas para ayudarlo a producir software más robusto y confiable.
fuente
Como todos los demás han hecho un gran trabajo al explicar los problemas de mantenimiento con el código duplicado, solo diré esto:
Gran parte de la programación requiere que pienses en el futuro, no solo en el presente inmediato. Tiene razón en que copiar y pegar es más fácil ahora, pero la afirmación de que es poco probable que tenga que volver a editar esto en el corto plazo " muestra que no está pensando correctamente. Sí, puede ganar un poco de tiempo con un copiar y pegar rápido y sucio, pero al hacerlo, está demostrando que no puede mirar más allá de su problema inmediato y pensar en el mañana. ¿Está seguro de que nunca necesitará volver a visitar este código? ¿Sabe con certeza que hay ¿No hay errores? ¿Puede garantizar al 100% que no necesitará volver a visitarlo cuando necesite implementar su próximo conjunto de características? Esos son problemas para mañana y deben tenerse en cuenta cuando diseñe hoy.
Por supuesto, hay momentos en que será necesario copiar / pegar. Como desarrollador de UI, descubrí que hay ocasiones en que tengo que violar el principio DRY. Es una mierda, me estremezco cada vez que sucede, y afortunadamente, es raro. Pero no suceda.
La diferencia es que cuando se viola DRY, debe tener una razón muy convincente para hacerlo, y la afirmación: es más difícil descubrir cómo hacer que todo eso se convierta en un patrón directo no es realmente uno de ellos. A menos que esté bajo una crisis de tiempo masiva y haga que su jefe grite para obtener algo en las próximas horas o perderá su trabajo, no creo que esta sea una justificación válida.
No lo tomes a mal: no estoy tratando de castigarte o castigarte, sino de tratar de hacerte ver dónde está mal tu mentalidad. Los programadores invierten en la pereza futura; DRY es una forma de lograr eso. El trabajo que realiza hoy para resolver un problema de diseño difícil dará sus frutos mañana.
fuente
Si este es realmente el caso, es posible que pueda salirse con la suya, pero la mayoría de las veces estará trabajando en un código que debe mantenerse. Eso significa ampliar la funcionalidad, corregir errores y otras mejoras. Si tiene pequeñas variaciones del mismo código en 10 lugares diferentes, y un día vuelve a ese código y necesita hacer un cambio, ahora tiene la tarea propensa a errores de hacer el mismo cambio en 10 lugares diferentes (Lo sentimos, hay fueron 11 lugares, olvidaste uno y ahora tienes un error).
Si puede generalizar qué problema está tratando de resolver, puede hacer que su código sea más fácil de extender y solucionar si aparecen errores.
fuente
Como dije en la respuesta a otra pregunta, mi enfoque es el siguiente:
Es decir, hasta 2, otro principio (YAGNI) gana a DRY. Pero a partir de 3 (¡o 4 si soy realmente vago!) Parece que lo voy a necesitar y por eso sigo SECO.
Actualizar
Algunas ideas adicionales de mi experiencia reciente. Tuve que adaptar / integrar dos componentes A y B desarrollados por otro equipo en nuestro producto. Primero: los dos componentes A yb B son muy similares entre sí, por lo que ya me molestó el hecho de que tenían una arquitectura algo diferente. Segundo: tuve que adaptarlos, así que me hubiera encantado usar subclases y anular solo lo que realmente necesitaba.
Así que comencé a refactorizar estos dos componentes (cada uno de los cuales consta de aproximadamente 8 clases de C ++): quería tener una arquitectura común para A y B, y luego agregar las características que necesitamos definiendo subclases. De esta manera, nuestros dos nuevos componentes A 'y B' se habrían derivado de los existentes.
Después de dos semanas tratando de obtener una estructura común y bien definida del código existente y de tener que explicar durante nuestras reuniones diarias que estaba progresando poco porque el código original era demasiado desordenado, hablé con mi jefe. Observamos que no necesitaríamos más que estos dos nuevos componentes A 'y B' (no iban a haber cuatro o seis de ellos, solo esos dos).
Ok, que así sea: hice una copia masiva y cambié el nombre de las clases de A y B y comencé a adaptar la copia del código. Lo puse a trabajar en dos semanas más (todavía estoy arreglando algunos errores).
Ventajas: Tenemos la funcionalidad casi terminada ahora y cuando hemos solucionado todos los errores, hemos terminado. Hemos guardado todas las refactorizaciones y pruebas de A y B.
Desventajas: Hace dos semanas, el otro equipo cambió otro componente C, que es usado por A y B. Adaptaron A y B, pero A 'y B' también se rompieron y tuvimos que cambiarlos nosotros mismos. Esto introdujo un nuevo error que tuvimos que corregir. Este trabajo adicional probablemente habría sido innecesario si A 'y B' hubieran compartido la mayor parte de su código con A y B.
Entonces: la duplicación de código siempre es peligrosa. Creo que siempre se trata de encontrar compensaciones y, a menudo, no es fácil.
fuente
Solo para aclarar, ya que no encuentro esto en ninguna de las otras respuestas:
El principio DRY mencionado por Andy Hunt y Dave Thomas no se limita a evitar la duplicación de código. También aboga por la generación de código y cualquier proceso de automatización. Irónicamente, los resultados de la generación de código podrían incluso ser códigos duplicados ...
La razón por la que ya se ha explicado a fondo en las otras respuestas, pero el comentario de Falcon lo resume bastante bien en mi humilde opinión:
fuente
Hay demasiada SECA. Cuando esto sucede, dos conceptos que en algún momento parecen ser lo suficientemente similares como para justificar el código de factorización (1) pueden luego resultar lo suficientemente diferentes como para merecer implementaciones separadas.
En otras palabras, el acoplamiento SECO y suelto a veces entra en conflicto. Si espera que doStuff1 y sus amigos diverjan con cada nueva versión del software, está bien duplicar su código.
En mi experiencia, puede ser difícil juzgar hacia dónde se dirige su software en el futuro, y por esta razón, DRY es a menudo una opción segura.
El código que se ha "secado" en exceso generalmente tiene un flujo de control complejo y demasiados parámetros. Lo que inicialmente era una función simple se extendió más tarde para admitir una nueva funcionalidad controlada por un parámetro adicional. Después de dos o tres iteraciones, la función ya no se puede mantener. Soluciona un error que ocurre en una configuración e introduces nuevos errores en otras configuraciones.
Es comprensible que la calidad del código a menudo disminuya a medida que el código evoluciona, pero he visto casos en los que una función multiparamétrica con espagueti if-then-else en el cuerpo fue el resultado de un esfuerzo de refactorización bien intencionado pero mal realizado.
(1) Estoy usando la palabra "código", pero esto también se aplica al diseño.
fuente
Tengo que mencionar los problemas con DRY en el mundo de la base de datos relacional. Las bases de datos están diseñadas para funcionar de manera rápida y correcta mediante la lógica basada en conjuntos y mediante consultas que se pueden modificar. Los principios DRY a menudo hacen que el desarrollador escriba consultas no Sargable o use la lógica Row-by-agonizing-Row para aprovechar el código existente en múltiples situaciones. DRY y la optimización del rendimiento a menudo están en desacuerdo y, en el mundo de las bases de datos, el rendimiento suele ser mucho más crítico que la capacidad de mantenimiento. Esto no significa que no deba usar los principios DRY en absoluto, solo que debe ser consciente de cómo afectará la usabilidad general de la base de datos. Los desarrolladores de aplicaciones son SECOS en primer lugar y el rendimiento en segundo lugar, los desarrolladores de bases de datos piensan en primer lugar la integridad de los datos, el rendimiento en segundo lugar, la seguridad de los datos en tercer lugar (el rendimiento y la seguridad pueden cambiar de lugar en algunos sistemas).
En general, he notado que cuantas más capas de abstracción pongas en las consultas de la base de datos, más lentas se volverán. No estoy diciendo que no quisiera que las personas que diseñan los programas de la base de datos no hicieran un mejor trabajo al permitir que los desarrolladores usen DRY sin afectar lo bien que funciona la base de datos, pero no diseño software de base de datos a ese nivel , entonces quizás el conflicto entre la abstracción y el rendimiento en la base de datos es más difícil de arreglar de lo que supongo. Sin embargo, tenemos que trabajar con los sistemas tal como están construidos actualmente. Podemos pedir una mejor implementación de los principios de DRY en futuras versiones que también no afectarán el rendimiento (y ha mejorado a través de los años pero sigue siendo problemático), pero mientras tanto debemos considerar si DRY es el movimiento correcto para esta base de datos. en este momento.
Pero a menudo las mismas características que desea utilizar para garantizar el cumplimiento del principio DRY son las que causan tremendos problemas a la base de datos. No digo que nunca uses DRY, pero no te excedas.
Ejemplos de lo que estoy hablando. Debe importar un millón de registros una vez al mes. Los registros ya se pueden agregar manualmente a través de la interfaz de usuario llamando a un proceso almacenado. Este proceso, debido a que fue diseñado para importar registros únicos, solo agrega un registro a la vez. Usando DRY para evitar tener el código de inserción en dos lugares, escribe un cursor para llamar al proceso repetidamente en lugar de escribir las importaciones basadas en conjuntos que necesita. El tiempo para la importación va de los 30 minutos que tomaría usar la lógica basada en conjuntos a 18 horas. Ahora, la forma correcta de adherirse a DRY en este caso sería arreglar el proceso para manejar múltiples importaciones de registros. Desafortunadamente, a menudo es imposible o muy difícil enviar una matriz a un proceso (dependiendo del back-end de db) y al cambiar el proceso, terminas rompiendo la aplicación.
Las funciones escalares y las funciones con valores de tabla también se utilizan para implementar los principios DRY y, una vez más, pueden afectar seriamente el rendimiento, especialmente si necesita usarlas de una manera que evite que los índices sean útiles.
Las vistas también son buenas para implementar DRY. Sin embargo, si implementa DRY mediante el uso de vistas que llaman vistas que llaman a otras vistas, llegará rápidamente al punto en que las consultas se agotarán bajo carga. De hecho, puede terminar necesitando generar conjuntos de datos de millones de registros cuando solo necesita tres al final. Por lo tanto, una vista de un nivel de un conjunto complejo de combinaciones para implementar DRY puede ser excelente (yo mismo tengo una que usamos para asegurarnos de que todos los informes financieros usen el mismo conjunto de tablas y cálculos de ciertas cosas), más de dos niveles y debe considerar si está creando un desastre de rendimiento.
fuente
No veo los puntos clave de mi respuesta arriba, así que aquí va. No mires DRY tanto como una regla contrahaciendo algo. Puede estar redactado de esa manera, pero realmente puede servir a un propósito bastante diferente y positivo. Es una señal para detenerse, pensar y encontrar una mejor respuesta. Me reta a buscar oportunidades para diseñar una mejor solución. Es el lado bueno de un mal olor en mi código lo que me induce a repensar mi diseño y me hace hacerlo mucho mejor. DRY no se trata solo de una infracción de sintaxis bitty bitty. Me reta a modularizar. Me reta a hacer componentes. Señala la repetición que me recuerda pensar en usar plantillas y generación de código en lugar de la fuerza bruta y la ignorancia. Me ayuda a descubrir que debería encontrar algo de tiempo para automatizar mi automatización. ¡Te lleva a un estilo de vida parsimonioso! Le ayuda a pasar más tiempo haciendo cosas nuevas y geniales en lugar de detalles aburridos y viejos. ¡Y te da buenos modales, buen aliento y un estilo de vida saludable! Bueno, tal vez me extravío un poco ...
fuente
Tengo un antiguo proyecto heredado, donde a algunos de los desarrolladores anteriores no les importaba DRY en absoluto. Entonces, toda la base de código estaba abarrotada de métodos auxiliares como GetSystemTimeAsString (), LogToFile () y muchas otras cosas. Algunos métodos fueron ligeramente personalizados para necesidades especiales, pero la mayoría fueron solo copiar y pegar.
Desafortunadamente, algunos de los métodos tenían errores sutiles como la matriz de caracteres no lo suficientemente largos en algunos casos, usando cosas inseguras como strcpy (), etc.
Por lo tanto, fue una verdadera PITA encontrar todos los fragmentos de código, armonizarlos y corregir los errores. Y todavía estamos armonizando y arreglando cosas.
Nunca se sabe, si cometió un error en su primer método y luego tuvo que arreglarlo varias veces, porque acaba de copiarlo. Y si desea utilizar algunos de los métodos más adelante, ¿cómo sabe cuál de los 5 métodos en la base de código es el adecuado para su caso ahora? Así que solo copia uno, personalízalo y aquí comienza de nuevo ...
fuente
Sí, no se preocupe por DRY si está escribiendo un código desechable .
Pero DRY es importante, por supuesto, si planea mantener el código.
fuente
La fraseología "no te repitas" es un poco simplista. Lo importante es "evitar tener una pieza de información potencialmente modificable encapsulada en dos lugares independientes ".
Si se supone que un programa procesa widgets, cada uno con tres woozles, y son muchos bucles de la forma
entonces la expectativa de que se espera que los widgets contengan tres woozles se encapsularía en cada uno de esos bucles, y actualizar el código para acomodar cualquier otro número de woozles por widget podría ser difícil. Por el contrario, si uno dijera
y cada ciclo fue reescrito
Tal diseño podría hacer que sea muy fácil cambiar el número de woozles por widget.
Sin embargo, es importante tener en cuenta que si bien es deseable consolidar información como el número de woozles por widget en un solo punto, no siempre es práctico. A veces puede ser necesario codificar la lógica, que solo funcionará si las cosas son de un tamaño particular. Por ejemplo, si cada woozle tiene un valor y uno quiere encontrar la mediana asociada con un widget en particular, puede ser posible ordenar los valores y tomar el medio, y este enfoque funcionaría con cualquier número de woozles, pero la lógica que está escrito a mano específicamente para encontrar la mediana de tres elementos podría ser significativamente más rápido.
Si bien tener una constante WOOZLES_PER_WIDGET puede hacer que el código sea más legible, debe comentarse para dejar en claro que su valor no se puede cambiar sin realizar otros ajustes en la lógica del programa. En ese caso, la lógica que está codificada para tres elementos y la constante WOOZLES_PER_WIDGET estaría duplicando la información "cada widget tiene tres woozles", pero los beneficios de tal duplicación (mayor velocidad de ejecución) podrían superar el costo.
fuente
Si bien estoy de acuerdo con los comentarios de otros pósters sobre mantenibilidad, etc., todos los cuales son válidos.
Me gustaría agregar una pequeña voz disidente al debate.
fuente
<tl;dr>
No pude leer todas las respuestas repetidas, por lo que es posible que me haya perdido algo (y lo repetiré yo mismo <= ¿ves lo que hice aquí?).
¡Aquí está la lista de cosas que son increíbles para prevenir la duplicación de código!
doFoo1(a, b)
, hay una mejor posibilidad de que muchas de sus molestas fallas y casos extremos se descubran y resuelvan. Si todos copian el código y creandoFoo2(specialA)
...doFuu2^n(a, b, c)
entonces duplicaron los problemasdoFoo1
y concretamente crearon mucho más trabajo.</tl;dr>
Versión larga:
El problema con la duplicación de código es que "crece exponencialmente" (en otras palabras, se expande rápidamente) porque cuando duplica el código, sin saberlo, otorga permiso a otros (por ejemplo, ya no está en posición de juzgarlos) y los animas a hacer lo mismo. También hace que sea más difícil no hacerlo porque es más difícil detectar y reutilizar código útil cuando hay muchas repeticiones redundantes confusas en la fuente. Especialmente si el código aún no se extrae en una función con el nombre adecuado. Entonces, si enfrenta un problema común simple de resolver, es probable que escriba usted mismo un código que lo resuelva ... Y probablemente no podrá verificar algunos casos extremos, agregando más código no probado con errores.
Otra cosa es que para un novato esto puede sonar como un problema que solo afectará a las grandes empresas, pero descubrí que afecta a las pequeñas empresas de manera muy grave (como en 10,000 líneas de código duplicado del lado del servidor). Es un estado mental. No solo debes dominar DRY, sino esforzarte por alentar a otros a hacer lo mismo; porque de lo contrario te condenarás a la mayoría del código duplicado. Cuando los medios de DRY están a la mano y se aplican, es mucho más fácil aplicarlos. Cuando hay mucho código duplicado, es mucho más fácil aplicar soluciones de copiar y pegar.
Cosas que encuentro dañinas en la duplicación de código:
Últimas notas sobre la prevención de duplicación de código demasiado entusiasta y resumiendo las cosas:
Esto también se ha dicho antes, pero a veces evitar la duplicación hace que te "inclines hacia atrás" y hagas cosas que son demasiado sofisticadas (o sin importancia) para que otros las entiendan. Escribir código ilegible (o como lo llamamos en broma código de "preservación del trabajo") es un problema en sí mismo, incluso cuando no se trata de evitar la duplicación de código. Sin embargo, creo que si se inculca la infraestructura correcta y las mejores prácticas desde el principio, es mucho más fácil evitar la duplicación de código y las personas a menudo pueden evitar hacer cosas poco intuitivas para evitar futuros montones innecesarios e ilegibles de trabajo realizado para la prevención de la duplicación de código si haces las cosas bien desde ese principio.
¿Qué está haciendo las cosas bien? Bueno, esa es una pregunta difícil de responder, pero una cosa es definir qué métodos son necesarios para el proyecto y ver lo que ya ha sido implementado por otros (fuera y) dentro de la empresa y reutilizarlo cuando sea posible; documentando todo lo que agregas a la base de código e intentando que sea una muesca más genérica de lo que tiene que ser, pero eso es todo. No exagere los patrones de diseño solo para hacer que el código sea flexible donde no es necesario.
fuente