Pregunta

Generalmente se acepta que copiar y pegar la programación es una mala idea, pero ¿cuál es la mejor manera de manejar una situación en la que tiene dos funciones o bloques de código que realmente hacen tienen que ser diferentes en ¿Unas pocas maneras de generalizarlos extremadamente desordenados?

¿Qué sucede si el código es sustancialmente el mismo, excepto por unas pocas variaciones menores, pero esas pocas variaciones menores no están en cosas que son fáciles de eliminar ya sea agregando un parámetro, métodos de plantilla o algo por el estilo? / p>

Más en general, ¿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 realmente justificada?

¿Fue útil?

Solución

He escuchado que la gente dice que se copiará y se pegará una vez (limitando la duplicación de código a un máximo de dos instancias), ya que las abstracciones no dan resultado a menos que use el código en tres lugares o más. () Yo, intento convertirlo en un buen hábito de refactorizar tan pronto como veo la necesidad.

Otros consejos

Haz esta pregunta sobre tus funciones

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

Por supuesto que a veces es aceptable. Es por eso que la gente guarda archivos de fragmentos. Pero si está cortando y pegando el código muy a menudo, o con más de unas pocas líneas, debería pensar en convertirlo en una subrutina. ¿Por qué? porque hay probabilidades de que tengas que cambiar algo, y de esta manera, solo necesitas cambiarlo una vez.

El caso intermedio es usar una macro si tiene disponible.

Sí, y es exactamente lo que dices; Variaciones menores pero difíciles de factor. No te flageles a ti mismo si realmente es lo que requiere la situación.

Re es cut-and-past 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, normalmente es mejor extraer los puntos comunes.

Re segmentos que parecen, pero no exactamente iguales:

Si la diferencia está en los datos, refactorice extrayendo la función y utilizando la diferencia en los datos como parámetros (si hay demasiados datos para pasar como un parámetro, considere agruparlos en un objeto o estructura). Si la diferencia está en algún proceso de la función, refactorice utilizando un patrón de comando o una plantilla abstracta. Si aún 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 & amp; diff2A siempre están juntos, y diff1B & amp; diff2B siempre están juntos, luego diff1A & amp; diff2A puede estar contenido en una clase de comando o una implementación de plantilla abstracta, y diff1B & amp; diff2B en otro.

Sin embargo, si hay varias combinaciones (es decir, diff1A & amp; diff2A, diff1A & amp; diff2B, diff1B & amp; diff2A, diff1B & amp; diff2B), es posible que desee reconsiderar su función porque puede estar tratando de manejar demasiadas responsabilidades propias.

Re declaraciones SQL:

El uso de lógicas (si-lo demás, bucles) para construir su SQL sacrifica dinámicamente la legibilidad. Pero crear todas las variaciones de SQL sería difícil de mantener. Así que cumple a mitad de camino y usa Segmentos SQL. Extraiga puntos en común como segmentos de SQL y cree todas las variaciones de SQL con esos segmentos de 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 el código base de mi empresa, tenemos una serie de aproximadamente 10 declaraciones SQL grandes y peludas que tienen un alto grado de compatibilidad. Todas las declaraciones tienen un núcleo común, o al menos un núcleo que solo difiere por una palabra o dos. 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 declaraciones SQL como conjuntos en un diagrama de Venn con una superposición significativa.

Elegimos codificar estas declaraciones para evitar cualquier duplicación. Entonces, hay una función (técnicamente, un método Java) para construir la declaración. Toma algunos parámetros que explican la palabra o dos de la diferencia en el núcleo común. Luego, toma un funtor para construir los apéndices, que por supuesto también está parametrizado con más parámetros para pequeñas diferencias y más funtores para más apéndices, y así sucesivamente.

El código es inteligente en el sentido de que ninguno de los SQL se repite. Si alguna vez necesita modificar una cláusula en el SQL, modifíquela en un solo lugar y las 10 sentencias de SQL se modifican en consecuencia.

Pero el hombre es el código difícil de leer. La única forma de averiguar qué SQL se ejecutará en un caso determinado es pasar por un depurador e imprimir el SQL una vez que se haya ensamblado por completo. Y descifrar cómo una función particular que genera una cláusula encaja en el panorama general es desagradable.

Desde que escribí esto, a menudo me pregunto si habríamos estado mejor cortando y pegando la consulta SQL 10 veces. Por supuesto, si hiciéramos esto, cualquier cambio en el SQL podría ocurrir 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 solo lugar probablemente superaría las desventajas de cortar y pegar el SQL.

Como sugiere Martin Fowler,

hazlo una vez, bien.

hazlo dos veces, empieza a oler.

hazlo tres veces, es hora de refactor .


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

Tres golpes y te refactorizas .

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

ABSOLUTAMENTE NUNCA ...

:)

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

Si es la única forma de hacerlo, entonces adelante. Muchas veces (dependiendo del idioma), puede satisfacer cambios menores en 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 que es necesario copiar / pegar. No uses un camino extraño y complicado para evitarlo.

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

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

¿Está fuera de la pereza? " Blah blah, he hecho esto antes ... solo estoy cambiando unos pocos nombres de variables ... hecho "

No es un 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 por el camino.

¿Es porque no entiendes? "Maldición ... no entiendo cómo funciona esa función, pero me pregunto si funcionará en mi código ..." ¡Que podría! Esto puede ahorrarle tiempo en el momento inmediato cuando está estresado porque tiene una fecha límite a las 9 a.m. y está mirando fijamente a un reloj alrededor de las 4 a.m.

¿Comprenderás este código cuando regreses a él? Incluso si lo comentas? No, en serio, después de miles de líneas de código, si no comprende lo que hace el código al escribirlo, ¿cómo entenderá el hecho de regresar semanas, meses después? Intenta aprenderlo, a pesar de toda tentación de lo contrario. Escríbelo, esto ayudará a memorizarlo. Cada línea que escriba, pregúntese qué está haciendo esa línea y cómo contribuye al propósito general de esa función. Incluso si no lo aprendes al revés, es posible que tengas la oportunidad de reconocerlo cuando regreses a él más adelante.

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ánta gente no lo hace.

Evito cortar y pegar como la plaga. Es incluso peor que su primo clon y modificador. Si se enfrenta a una situación como la suya, siempre estoy listo para usar un procesador de macros u otro script para generar las diferentes variaciones. En mi experiencia, un único punto de verdad es enormemente importante.

Desafortunadamente, el procesador de macros C no es muy bueno para este propósito debido a los requisitos de cotización molestos 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 usaré el preprocesador de C a pesar de sus deficiencias porque no agrega otro elemento a la cadena de herramientas y, por lo tanto, no requiere cambiar el proceso de compilación o Makefiles.

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

La mejor manera (además de convertir en funciones comunes o usar macros) es colocar comentarios. Si comenta de dónde se copia el código, qué es lo que tiene en común, y las diferencias, y la razón para hacerlo. ... entonces estarás bien.

Si encuentra que tiene funciones que son prácticamente las mismas, pero en diferentes escenarios requieren pequeños ajustes, el problema es su diseño. Utilice polimorfismo y composición en lugar de banderas o copie y pegue.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top