what sourcemaking software programming poo patterns pattern ood latest gof edition design-patterns copy-paste

design-patterns - software - sourcemaking



¿Es aceptable la codificación de copiar y pegar? (14)

ABSOLUTAMENTE NUNCA ...

:)

Podría publicar el código en cuestión y ver que es más fácil de lo que parece

En general, se acepta que copiar y pegar programación es una mala idea, pero ¿cuál es la mejor manera de manejar una situación en la que tienes dos funciones o bloques de código que realmente necesitan ser diferentes de pocas maneras para generalizarlos y hacerlos extremadamente complicados?

¿Qué pasa si el código es sustancialmente el mismo, excepto por algunas variaciones menores, pero esas pocas variaciones menores no se encuentran en cosas que son fáciles de factorizar mediante la adición de un parámetro, métodos de plantilla o algo así?

En términos más generales, ¿alguna vez te has encontrado con una situación en la que admitirías que una pequeña codificación de copiar y pegar estaba verdaderamente justificada?


¡Me alegra que este sea etiquetado como subjetivo porque ciertamente lo es! Este es un ejemplo excesivamente vago, pero me imagino que si tiene suficiente código duplicado, podría abstraer esas secciones y mantener las diferentes partes diferentes. El objetivo de no copiar y pegar es que no termines teniendo un código que es difícil de mantener y frágil.


Haga esta pregunta sobre sus funciones

"Si este pequeño requisito cambia, ¿tendré que cambiar ambas funciones para satisfacerlo?"


La mejor forma (además de convertir en funciones comunes o usar macros) es incluir comentarios. Si comenta dónde se copió el código, y a qué se debe, y las diferencias, y la razón para hacerlo ... entonces estarás bien.


Sí, y es exactamente como dices; variaciones menores pero difíciles de factorizar. No flagele usted mismo si es realmente lo que la situación requiere.


Si encuentra que tiene funciones que son en su mayoría iguales, pero en diferentes escenarios requieren pequeños retoques, su diseño es el problema. Usa polimorfismo y composición en lugar de banderas o copiar y pegar.


Si es la única forma de hacerlo, entonces hazlo. Muchas veces (dependiendo del idioma), puede satisfacer cambios menores a la misma función con un argumento opcional.

Recientemente, tuve una función add () y una función edit () en un script PHP. Ambos hicieron prácticamente lo mismo, pero la función edit () realizó una consulta UPDATE en lugar de una consulta INSERT. Acabo de hacer algo como

function add($title, $content, $edit = false) { # ... $sql = edit ? "UPDATE ..." : "INSERT ..."; mysql_unbuffered_query($sql); }

Funcionó muy bien, pero hay otros momentos en los que copiar / pegar es necesario. No use un camino extraño y complicado para evitarlo.


Re es siempre aceptable:

Sí. Cuando el segmento es ligeramente diferente y está haciendo sistemas desechables (sistemas que están ahí por un período de tiempo muy corto y no necesitarán mantenimiento). De lo contrario, generalmente es mejor extraer los elementos comunes.

Re segmentos que se parecen pero no exactamente iguales:

Si la diferencia está en los datos, refactor extraiga la función y utilice la diferencia de datos como parámetros (si hay demasiados datos para pasar como parámetro, considere agruparlos en un objeto o estructura). Si la diferencia está en algún proceso de la función, refactorice usando un patrón de comando o una plantilla abstracta. Si todavía es difícil refactorizar incluso con estos patrones de diseño, entonces su función podría estar tratando de manejar muchas responsabilidades por sí misma.

Por ejemplo, si tiene un segmento de código que difiere en dos segmentos: diff # 1 y diff # 2. Y en diff # 1, puede tener diff1A, o diff1B, y para diff # 2 puede tener diff2A y diff2B.

Si diff1A y diff2A siempre están juntos, y diff1B y diff2B siempre están juntos, entonces diff1A y diff2A pueden estar contenidos en una clase de comando o en una implementación de plantilla abstracta, y diff1B & diff2B en otra.

Sin embargo, si hay varias combinaciones (es decir, diff1A y diff2A, diff1A y diff2B, diff1B y diff2A, diff1B y diff2B), puede reconsiderar su función porque puede estar tratando de manejar demasiadas responsabilidades por sí mismo.

Re declaraciones SQL:

El uso de lógicas (if-else, loops) para construir su SQL sacrifica dinámicamente la legibilidad. Pero crear todas las variaciones de SQL sería difícil de mantener. Así que conoce a mitad de camino y usa Segmentos SQL. Extraiga elementos comunes como segmentos SQL y cree todas las variaciones SQL con esos segmentos SQL como constantes.

Por ejemplo:

private static final String EMPLOYEE_COLUMNS = " id, fName, lName, status"; private static final String EMPLOYEE_TABLE = " employee"; private static final String EMPLOYEE_HAS_ACTIVE_STATUS = " employee"; private static final String GET_EMPLOYEE_BY_STATUS = " select" + EMPLOYEE_COLUMNS + " from" + EMPLOYEE_TABLE + " where" + EMPLOYEE_HAS_ACTIVE_STATUS; private static final String GET_EMPLOYEE_BY_SOMETHING_ELSE = " select" + EMPLOYEE_COLUMNS + " from" + EMPLOYEE_TABLE + " where" + SOMETHING_ELSE;


En la base de código de mi empresa, tenemos una serie de aproximadamente 10 grandes sentencias SQL peludas que tienen un alto grado de similitud. Todas las declaraciones tienen un núcleo común, o al menos un núcleo que solo difiere en una o dos palabras. Luego, podría agrupar las 10 declaraciones en 3 o 4 agrupaciones que agregan apéndices comunes al núcleo, nuevamente con tal vez una o dos palabras diferentes en cada apéndice. En cualquier caso, piense en las 10 sentencias de SQL como conjuntos en un diagrama de Venn con una superposición significativa.

Elegimos codificar estas declaraciones de tal manera que se evite cualquier duplicación. Entonces, hay una función (técnicamente, un método de Java) para construir la declaración. Se necesitan algunos parámetros que den cuenta de la palabra o dos de diferencia en el núcleo común. Luego, se necesita un funtor para construir los apéndices, que por supuesto también está parametrizado con más parámetros para diferencias menores y más funtores para más apéndices, y así sucesivamente.

El código es inteligente ya que ninguna parte del SQL se repite alguna vez. Si alguna vez necesita modificar una cláusula en el SQL, la modifica en un solo lugar y las 10 sentencias SQL se modifican en consecuencia.

Pero el código es difícil de leer. La única forma de averiguar qué SQL se va a ejecutar para un caso determinado es pasar por un depurador e imprimir el SQL después de que se haya ensamblado por completo. Y descubrir cómo una función particular que genera una cláusula se ajusta a la imagen más grande es desagradable.

Desde que escribí esto, a menudo me he preguntado si hubiera sido mejor cortar y pegar la consulta SQL 10 veces. Por supuesto, si hiciéramos esto, cualquier cambio en el SQL podría tener lugar en 10 lugares, pero los comentarios podrían ayudarnos a señalar los 10 lugares para actualizar.

El beneficio de tener el SQL comprensible y todo en un lugar probablemente supere las desventajas de cortar y pegar el SQL.


Escuché a personas decir que copiarán y pegarán una vez (lo que limita la duplicación del código a dos instancias como máximo), ya que las abstracciones no dan resultado a menos que uses el código en tres lugares o más. () Yo intento hacer una buena costumbre de refactorizar tan pronto como veo la necesidad.


Por supuesto, a veces es aceptable. Es por eso que la gente guarda archivos de fragmentos. Pero si corta y pega código muy a menudo, o con más de unas pocas líneas, debe pensar en convertirlo en una subrutina. ¿Por qué? porque las probabilidades de que tenga que cambiar algo, y de esta manera, solo necesita cambiarlo una vez.

El caso medio es usar una macro si tienes tal disponible.


Como sugiere Martin Fowler,

hazlo una vez, bien.

hazlo dos veces, comienza a oler.

hazlo tres veces, es hora de refactorizar .

EDITAR: en respuesta al comentario, el origen del consejo es Don Roberts:

Tres golpes y tu refactor .

Martin Fowler describe eso en Refactorización capítulo 2, sección La regla de los tres (página 58).


Evito cortar y pegar como la peste. Es incluso peor que su primo clonar y modificar. Si se enfrenta con una situación como la suya, siempre estoy listo para usar un macroprocesador u otro script para generar las diferentes variaciones. En mi experiencia, un solo punto de verdad es muy importante.

Desafortunadamente, el procesador de macro C no es muy bueno para este propósito debido a los molestos requisitos de cotización para nuevas líneas, expresiones, declaraciones y argumentos. Odio escribir

#define RETPOS(E) do { if ((E) > 0) then return; } while(0)

pero esa cita es una necesidad. A menudo utilizaré el preprocesador C a pesar de sus deficiencias porque no agrega otro elemento a la cadena de herramientas, por lo que no es necesario cambiar el proceso de compilación o los archivos Makefiles.


  1. El código bueno es código reutilizable.
  2. No reinventar la rueda.
  3. Los ejemplos existen por una razón: para ayudarlo a aprender, e idealmente, codificar mejor.

¿Deberías copiar y pegar? ¡A quien le importa! Lo importante es por qué estás copiando y pegando. No estoy tratando de ponerme filosófico con nadie aquí, pero pensemos sobre esto de manera práctica:

¿Es por pereza? "Bla, bla, he hecho esto antes ... solo estoy cambiando algunos nombres de variables ... hecho".

No hay problema si ya era un buen código antes de copiarlo y pegarlo. De lo contrario, estás perpetuando el código de mierda por la pereza que te morderá el culo en el futuro.

¿Es porque no entiendes? "Maldición ... no entiendo cómo funciona esa función, pero me pregunto si funcionará en mi código ..." ¡Podría ser! Esto puede ahorrarle tiempo en el momento en que está estresado, tiene un plazo de entrega a las 9 a.m. y tiene los ojos enrojecidos a las 4:00 a.m.

¿Comprenderás este código cuando vuelvas a él? Incluso si lo comentas? No, en realidad, después de miles de líneas de código, si no comprende qué está haciendo el código mientras lo escribe, ¿cómo va a entender volver semanas semanas o meses después? Intenta aprenderlo, a pesar de todas las tentaciones de lo contrario. Tipearlo, esto ayudará a memorizarlo. Cada línea que escriba, pregúntese qué está haciendo esa línea y cómo contribuye al objetivo general de esa función. Incluso si no lo aprende internamente, es posible que tenga la oportunidad de reconocerlo como mínimo cuando regrese a él más tarde.

Entonces, ¿copiar y pegar código? Bien si eres consciente de las implicaciones de lo que estás haciendo. ¿De otra manera? No lo hagas Además, asegúrese de tener una copia de la licencia de cualquier código de terceros que copie y pegue. Parece de sentido común, pero te sorprendería saber cuántas personas no lo hacen.