¿Qué es refactorizar y qué es solo modificar código?

81

Sé que la refactorización es "cambiar la estructura de un programa para que la funcionalidad no cambie". Estaba hablando con algunos de los chicos con los que estoy trabajando en mi proyecto de último año en la universidad y me sorprendió que tuvieran una visión mucho más amplia (a falta de una palabra mejor) de la refactorización.

Considero que la refactorización son cosas como extraer métodos y cambiar el nombre de las clases. También sugirieron cosas como cambiar las estructuras de datos (como un Java LinkedLista un ArrayList), cambiar algoritmos (usar la clasificación por fusión en lugar de la clasificación por burbujas) e incluso reescribir grandes trozos de código como refactorización.

Estaba bastante seguro de que estaban equivocados, pero no pude dar una buena razón porque lo que estaban sugiriendo cambió el programa (y presumiblemente lo mejoró) sin cambiar su comportamiento. ¿Tengo razón y, lo que es más importante, por qué?

David Johnstone
fuente

Respuestas:

77

"Refactorización: mejora del diseño del código existente" de Martin Fowler es quizás LA referencia:

La refactorización es una técnica controlada para mejorar el diseño de una base de código existente. Su esencia es aplicar una serie de pequeñas transformaciones que preservan el comportamiento, cada una de las cuales "es demasiado pequeña para que valga la pena hacerla". Sin embargo, el efecto acumulativo de cada una de estas transformaciones es bastante significativo. Al hacerlo en pequeños pasos, reduce el riesgo de introducir errores. También evita que el sistema se rompa mientras realiza la reestructuración, lo que le permite refactorizar gradualmente un sistema durante un período de tiempo prolongado.

La refactorización va de la mano con las pruebas unitarias. Escriba pruebas antes de refactorizar y luego tendrá un nivel de confianza en la refactorización (proporcional a la cobertura de las pruebas).

Una buena referencia es: Información sobre refactorización

Trigo Mitch
fuente
16
La cita de Fowler es por supuesto relevante, y sí, va de la mano con las pruebas unitarias ... Pero, ¿esto realmente responde a las preguntas formuladas? ¿Los ejemplos mencionados son refactorización o simplemente modificando el código? ¿Quién tiene razón, el OP o sus colegas y por qué?
Jonik
36

Fowler traza una línea clara entre los cambios en el código que afectan su comportamiento y los que no. A los que no lo hacen, los llama "refactorización". Ésta es una distinción importante, porque si dividimos nuestro trabajo en actividades de modificación de código de refactorización y no refactorización (Fowler lo llama "usar sombreros diferentes"), podemos aplicar técnicas diferentes y apropiadas para el objetivo.

Si estamos haciendo una refactorización o una modificación de código para preservar el comportamiento:

  • todas nuestras pruebas unitarias deben pasar antes y después de la modificación
  • no deberíamos necesitar modificar ninguna prueba, ni escribir ninguna nueva
  • esperamos un código más limpio cuando hayamos terminado
  • no esperamos un nuevo comportamiento

Si estamos haciendo una modificación de código que cambia el comportamiento:

  • esperamos un nuevo comportamiento
  • deberíamos escribir nuevas pruebas
  • es posible que obtengamos un código más sucio cuando hayamos terminado (y luego deberíamos refactorizarlo)

Si perdemos de vista esta distinción, entonces nuestras expectativas para cualquier tarea de modificación de código dada son confusas y complejas, o al menos más confusas y más complejas que si la tuviéramos en cuenta. Por eso la palabra y su significado son importantes.

Carl Manaster
fuente
3
+1, exactamente. Especialmente la justificación que das; las confusas expectativas. Al escribir mi propia respuesta, tenía esto en mente, incluso si no lo escribí tan claramente :)
Jonik
18

Para dar mi vista:

Pequeños cambios incrementales que dejan el código en un mejor estado del que se encontró

Definitivamente si: cambios "cosméticos" que no están directamente relacionados con las funciones (es decir, no se pueden facturar como una solicitud de cambio).

Definitivamente no: reescribir partes grandes claramente viola la parte "pequeña, incremental". La refactorización se usa a menudo como lo opuesto a una reescritura: en lugar de hacerlo de nuevo, mejora lo existente.

Definitivamente Quizás: Reemplazar estructuras de datos y algoritmos es algo así como un caso límite. La diferencia decisiva aquí, en mi opinión, son los pequeños pasos: esté listo para entregar, esté listo para trabajar en otro caso.


Ejemplo: Imagine que tiene un módulo de aleatorización de informes que se ralentiza por el uso de un vector. Ha perfilado que las inserciones vectoriales son el cuello de botella, pero desafortunadamente el módulo se basa en la memoria contigous en muchos lugares, por lo que al usar una lista, las cosas se rompen silenciosamente.

Reescribir significaría deshacerse del Módulo y construir uno mejor y más rápido desde cero, simplemente recogiendo algunas piezas del anterior. O escribiendo un nuevo núcleo y luego ajustándolo al diálogo existente.

Refactorizar significaría tomar pequeños pasos para eliminar la aritmética del puntero, de modo que el cambio. Tal vez incluso cree una función de utilidad que envuelva la aritmética del puntero, reemplazando la manipulación directa del puntero con llamadas a esa función, luego cambie a un iterador para que el compilador se queje de lugares donde todavía se usa la aritmética del puntero, luego cambie a ay listluego elimine el función de ultilidad.


La idea detrás es que el código empeora por sí solo. Al corregir errores y agregar funciones, la calidad decae en pequeños pasos: el significado de una variable cambia sutilmente, una función obtiene un parámetro adicional que rompe el aislamiento, un bucle se vuelve un poco complejo, etc. Ninguno de estos es un error real, puede No dice un recuento de líneas que hace que el ciclo sea complejo, pero perjudica la legibilidad y el mantenimiento.

Del mismo modo, cambiar el nombre de una variable o extraer una función no son mejoras tangibles en sí mismas. Pero todos juntos, luchan contra la lenta erosión.

Como un muro de guijarros donde todos los días uno cae al suelo. Y todos los días, un transeúnte lo recoge y lo devuelve.

Peterchen
fuente
12

Con la definición de Martin Fowler en mente,

La refactorización es una técnica disciplinada para reestructurar un cuerpo de código existente, alterando su estructura interna sin cambiar su comportamiento externo.

... Creo que claramente tienes razón.

También sugirieron cosas como cambiar las estructuras de datos (como una LinkedList de Java a una ArrayList), cambiar algoritmos (usar la clasificación por fusión en lugar de la clasificación por burbujas) e incluso reescribir grandes trozos de código como refactorización.

Cambiar un algoritmo a algo mucho más rápido obviamente no es refactorizar, ¡porque se cambia el comportamiento externo! (Por otra parte, si el efecto nunca se nota, tal vez podría llamarlo refactorización después de todo, y también optimización prematura. :-)

Esta es una manía mía; es molesto cuando la gente usa el término de manera descuidada; incluso me he encontrado con algunos que podrían usar la refactorización para básicamente cualquier tipo de cambio o corrección. Sí, es una palabra de moda moderna y genial y todo eso, pero no hay nada de malo en términos simples y antiguos como cambio , reescritura o mejora del rendimiento . Deberíamos usarlos cuando sea apropiado, y reservar la refactorización para los casos en los que realmente esté mejorando la estructura interna de su software. Dentro de un equipo de desarrollo, especialmente, es importante tener un lenguaje común para discutir con precisión su trabajo.

Jonik
fuente
3
No estoy seguro de que hacer que el código sea más rápido califique como un cambio en el comportamiento externo ...
GalacticCowboy
2
Sí, veo tu punto, supongo que depende de qué ángulo lo mires. :) En cualquier caso, en mi opinión, al programar debes prestar atención a qué "sombrero" estás usando en cada momento, es decir, qué es exactamente lo que estás tratando de lograr. En otras palabras, debe separarse conscientemente cuando está agregando / arreglando una característica, cuando refactoriza y cuando optimiza (mejora el rendimiento). IIRC, Fowler también habla de esto en su libro definitivo sobre refactorización.
Jonik
1
Para la parte del cambio externo, podríamos reformular para decir: [...] sin cambiar su comportamiento externo si el comportamiento es importante. Si el rendimiento es importante de cualquier manera (más rápido o más lento), no realice ningún cambio que pueda afectarlo como parte de su fase de "refactorización".
Loki
11

Si cambia la interfaz de un fragmento de código, lo considero más que una refactorización.

El caso típico de refactorización es

  • "Oh, todas mis pruebas unitarias se ejecutan, pero creo que mi código podría hacerse más limpio"
  • Cambie el código para que sea más legible / claro / eficiente
  • Vuelva a ejecutar las pruebas unitarias (sin cambiar las pruebas) y compruebe que aún funcionan

Esto significa que el término refactorización es relativo a la interfaz que está discutiendo. es decir, podría estar refactorizando el código detrás de una interfaz, mientras cambia más extensamente el código de otra en un nivel inferior (¿tal vez esa distinción es lo que genera la confusión entre usted y sus colegas aquí?)

DanSingerman
fuente
7

Creo que tienes razón, pero discutir sobre el significado de una palabra no es particularmente interesante ni productivo.

cbp
fuente
6
Normalmente estaría de acuerdo con esto, pero la discusión surgió cuando estábamos revisando documentos que habíamos escrito, y creo que es bueno cuando hablamos de lo mismo cuando usamos las mismas palabras.
David Johnstone
4

http://en.wikipedia.org/wiki/Code_refactoring

La refactorización de código es el proceso de cambiar la estructura interna de un programa de computadora sin modificar su comportamiento funcional externo o la funcionalidad existente, con el fin de mejorar las propiedades internas no funcionales del software, por ejemplo, para mejorar la legibilidad del código, para simplificar la estructura del código, para cambiar el código. para adherirse a un paradigma de programación dado, para mejorar la mantenibilidad, para mejorar el rendimiento o para mejorar la extensibilidad.

Estoy de acuerdo en que la refactorización del código incluye romper el código existente. Solo asegúrese de tener pruebas unitarias para que no introduzca ningún error y el resto del código se compile. El uso de herramientas de refactorización como Resharper para C # hace que esto sea muy fácil.

  • Haciendo el código más comprensible
  • Limpiando el código y haciéndolo más ordenado
  • ¡Eliminando código! El código y los comentarios redundantes y no utilizados deben eliminarse
  • Mejorando el desempeño
  • Haciendo algo más genérico. Comience con lo más simple posible, luego refactorice para que sea más fácil de probar / aislar o genérico para que pueda funcionar de diferentes maneras a través del polimorfismo
  • Mantener el código SECO: no se repita, por lo que una sesión de refactorización podría implicar tomar un código repetido y refactorizarlo en un solo componente / clase / módulo.
superlógico
fuente
3

Yo discrepo :

En ingeniería de software, "refactorizar" el código fuente significa mejorarlo sin cambiar sus resultados generales [...]

Ya conoce los términos más precisos que se utilizan para los subconjuntos de refactorización, y sí, es un término muy general.

l0b0
fuente
1

Creo que nadie puede beneficiarse de una definición demasiado fuerte del término 'refactorización'. El límite entre cómo lo percibes y tus colegas es borroso y puede estar más cerca de su punto de vista o el tuyo dependiendo de muchos hechos. Dado que es dinámico, intentemos definirlo. En primer lugar, defina los límites del sistema o subsistema que está intentando refactorizar.

Si se trata de un método, mantenga fijo el nombre, los argumentos de entrada, el tipo de valor devuelto y posiblemente las declaraciones de lanzamiento. Aplique todos los cambios dentro del método sin cambiar la forma en que se ve fuera.

Si refactoriza una clase, corrija su API pública y use el cambio de nombre de las variables, los métodos de extracción y todas las demás técnicas disponibles cambian la clase para que sea más legible y / o más eficaz.

Si la parte del código que está refactorizando es un paquete o un módulo, haga la refactorización dentro de él, posiblemente cambiando el nombre de las clases, eliminando, introduciendo interfaces, empujando / tirando de código en super / subclases.

Boris Pavlović
fuente
0

Refactorización = mejorar los requisitos no funcionales manteniendo inalterados los funcionales.

Requisitos no funcionales = modularidad, capacidad de prueba, facilidad de mantenimiento, legibilidad, separación de preocupaciones, principios de Liskov, etc.

EnzoVici
fuente