Refactorización durante la programación

9

Cuando se me plantea un problema, particularmente cuando es de naturaleza complicada, trato de tomarme un tiempo para pensar en el enfoque que voy a tomar para resolver el problema. A pesar de esto, lo que sucede a menudo es que, mientras estoy programando la solución, empiezo a pensar en detalles del problema que me perdí, y ajusto el código en consecuencia.

Lo que resulta es un desastre de código que necesita ser refactorizado.

Quiero "refactorizar sobre la marcha", pero aunque parezca bastante fácil de hacer, me cuesta mucho hacerlo. Cuando el detalle que me perdí es pequeño, es tentador hacer una pequeña actualización de mi diseño, en lugar de borrar lo que ya he escrito y escribirlo como se supone que debe ser.

Suena como una pregunta con una respuesta obvia, pero ¿hay alguna técnica para usar para "refactorizar sobre la marcha"? Sé que este es un buen principio, pero fallo una y otra vez.

Kirby
fuente

Respuestas:

17

Probablemente la observación fundamental en el libro Refactoring de Fowler es que es mucho mejor separar los cambios que afectan el comportamiento del código de los cambios que no lo hacen. Aquellos en la última categoría, los llamamos "refactorización". Si mezclas estos cambios, los arruinas, así que no lo hagas. Puede alternar entre los modos de codificación y refactorización, pero no haga ambas cosas al mismo tiempo. Porque si lo intentas, no sabrás lo que hiciste mal.

Si intenta introducir un cambio que modifica el comportamiento, y el comportamiento no se modifica: hizo algo mal; revísalo y arréglalo. Tal vez solo retroceda.

Si intenta introducir una refactorización, y el comportamiento se modifica: hizo algo mal; revísalo y arréglalo. Tal vez solo retroceda.

No puede hacer esa evaluación simple si intenta ambos cambios a la vez. Tampoco puedes, si no tienes pruebas unitarias. Escríbelas Y haz una cosa a la vez.

Carl Manaster
fuente
1
Consejos interesantes ... Aunque, como todos los consejos, no 100%. Creo que este consejo funciona bien si realizas una refactorización relativamente pequeña de un código grande. Pero a veces las refactorizaciones radicales, como la reescritura casi completa, también están acompañadas de nuevas características, y es eficiente hacer ambas cosas al mismo tiempo.
Tomás
3
@Tomas: La pregunta era claramente sobre "refactorizar sobre la marcha", como fue la respuesta de Carl (+1 de mi parte). Una "reescritura casi completa" no es refactorizar ... es, bueno, una reescritura.
Amos M. Carpenter
1
No solo la codificación mixta con la refactorización arruinará las cosas, sino que también se realizarán diferentes refactorizaciones al mismo tiempo .
Rangi Lin
6

Esto es lo que hago a menudo: dejar notas como comentarios, o incluso código comentado. Cuando tenga el código descuidado funcionando, ahora puede reorganizarlo fácilmente.

Además, no hay nada inherentemente malo en la refactorización a medida que avanza, solo asegúrese de tener formas de probar que su código refactorizado no introduce nuevos errores antes de continuar.

Luke Dennis
fuente
convenido. A menudo es más fácil refinar el código de trabajo feo en un código hermoso que tratar de hacerlo desde cero. Solo tiene que ser honesto y darse el tiempo para limpiar su solución una vez que esté funcionando.
bunglestink
2
Sin embargo, tenga cuidado de no dejar demasiado código comentado, o de lo contrario se desordenará todo. En general, si ha reemplazado el código incorrecto, simplemente debe eliminarlo. Además, si se trata de un proyecto grupal, a los demás programadores generalmente no les gustará por cometer código comentado sin una razón excelente.
user24795
1
Mi enfoque: si no está seguro de lo que está haciendo, comente el código anterior. Incluya sus iniciales y la fecha . Si encuentra cosas que ya no parecen relevantes con una fecha anterior, elimínelas, especialmente si tiene sus iniciales. Si reactiva el código antiguo y no sabe por qué se desactivó, agregue una explicación completa para que cuando alguien (usted, quizás) más tarde quiera volver a ponerlo como estaba, sabrá que está en un ciclo de desarrollo infinito y saber cómo salir de eso.
RalphChapin
5

Otra buena regla general es que cada método debe hacer una sola cosa.

Si desglosa el procesamiento de lo que está tratando de implementar en pequeños pasos discretos y codifica los pasos como métodos, cuando sea el momento de re-factorizar, encontrará que la única refactorización que tiene sentido es cambiar el ordenar que se invoquen los métodos o dejar algunos métodos fuera de la ruta de ejecución.

Además, dado que sus métodos son discretos, puede re-factorizar cualquier método individual siempre que el valor devuelto sea el que debe ser, ninguno de los otros códigos será más sabio que se realizó un cambio.

Esto prepara el escenario para que realice mejoras incrementales en su código a lo largo del tiempo simplemente optimizando un método aquí y allá. La lógica general no cambia, solo los detalles de un paso en particular.

Si sigue esto desde los métodos hasta las clases, donde en sus clases hace o representa un solo objeto, encontrará que, al igual que con los métodos, su refactorización se convierte en una simple reorganización del orden en que sus clases se construyen y usan , y menos sobre cambiar realmente la lógica de detalles.

Sabe que ha alcanzado el punto óptimo cuando la especificación del programa cambia drásticamente y de repente, pero puede adaptarse a casi todos los cambios necesarios simplemente modificando las relaciones entre sus objetos, ¡sin cambiar su implementación interna en absoluto! ¡Se siente como si de repente adquirieras el nirvana!

¡Buena suerte y que todo tu código esté libre de errores!

Rodney

rpbarbati
fuente
3

La unidad que prueba el código podría ayudar.

Edson Medina
fuente
2

¿Es posible que esté escribiendo interfaces como si adoptara un enfoque de arriba hacia abajo para resolver un problema, pero luego lo implementara de abajo hacia arriba? Si es así, creo que esa es la falta de coincidencia lógica que le está causando problemas.

Según lo que está describiendo, podría ser mejor desglosar el problema para que pueda pensar en al menos una cosa que necesita hacer, y luego implementar eso. Piense en otra cosa que necesita hacer e implemente solo eso. Continúe, construya su solución de modo que escriba primero los fundamentos absolutos, luego los bloques de construcción que descansan sobre ellos y así sucesivamente hasta que obtenga la solución real. Tiendo a encontrar que la solución completa, incluso para problemas difíciles, tiende a acercarse sigilosamente y de repente salta al seguir ese tipo de enfoque.

Si mantiene los bloques de construcción intermedios enfocados lógicamente y mantiene deliberadamente cerrado y limitado cada segmento de código, entonces la cantidad de refactorización que deberá realizar siempre debe ser pequeña y aislada.

Tommy
fuente
Esto es lo que estoy haciendo, creo. A menudo escribo el esquema de lo que quiero hacer, como en el patrón de diseño del método de plantilla, por ejemplo. Luego implemento esos métodos. Cuando llego a los detalles, a veces descubro que algo no estaba bien en la plantilla original.
Kirby
1

... es tentador hacer una pequeña actualización de mi diseño, en lugar de borrar lo que ya he escrito y escribirlo como se supone que debe ser .

Nada se supone. Lo único que se supone es que:

  1. que puede hacer cualquier cosa que usted quiere
  2. su tiempo de vida / trabajo es limitado y solo puede alcanzar un número limitado de cosas.

¿Qué deseas? ¿Un proyecto de código limpio súper duper-excelente-limpio absolutamente perfecto, o tres proyectos terminados, solo 70% de elegancia de código en comparación con el primero, pero 3 veces el 95% del punto de vista del usuario? ¿Qué será más útil para el mundo, para su gente, para su misión?

¿Qué deseas?

Por supuesto, a largo plazo, podría ser rentable para refactorizar (reduce el tiempo mtce, etc.), pero solo a largo plazo. Nosotros, los programadores, a menudo estamos tentados a refactorizar y optimizar mucho antes de lo necesario. A menudo refactorizamos un código que no se desarrolla más, lo que significa tiempo perdido.

Yo uso mi propia regla de oro de 2-3. En el primer uso de un código particular, simplemente lo castigo lo antes posible. Casi nunca vale la pena pensar en un uso más general del mismo, porque todavía no tiene suficiente información sobre qué casos de uso podrían venir. Y también, esta parte del código podría nunca volver a usarse. Cuando se necesita el código por segunda vez, trato de hacer que el primer código sea más general para aplicar también a este caso, o elijo copiarlo (pero esto no me gusta). Con el tercer uso, creo que tengo suficiente información para refactorizar y, como esta parte del código parece ser realmente utilizada, creo que vale la pena.

Tomás
fuente
0

Lo que resulta es un desastre de código que necesita ser refactorizado.

Simplemente analice el problema hasta que lo entienda. Luego, realice una implementación nueva y correcta después de conocer su forma.

Para responder a su pregunta en términos más generales: Recuerde que los cambios necesarios generalmente causan menos fluctuaciones / pruebas si se realizan más temprano que tarde.

justin
fuente