software sinonimo refactory refactorizar refactorizacion ejemplos ejemplo codigo refactoring compiler-errors

refactoring - sinonimo - ¿La refactorización por errores de compilación es mala?



refactorizar sinonimo (13)

Cuando esté listo para leer libros sobre el tema, le recomiendo el " Código de Working Effectively with Legacy " de Michael Feather. ( Agregado por el no autor: también el clásico libro de Fowler " Refactorización " - y el sitio web Refactorización puede ser útil ) .

Habla sobre identificar las características del código que está trabajando antes de hacer un cambio y hacer lo que él llama refactorización de cero. Eso es refectoring para encontrar las características del código y luego arrojar los resultados de distancia.

Lo que está haciendo es usar el compilador como una autoprueba. Probará que su código compila, pero no si el comportamiento ha cambiado debido a su refactorización o si hubo algún efecto secundario.

Considera esto

class myClass { void megaMethod() { int x,y,z; //lots of lines of code z = mysideEffect(x)+y; //lots more lines of code a = b + c; } }

podrías refactorizar la adición

class myClass { void megaMethod() { int a,b,c,x,y,z; //lots of lines of code z = addition(x,y); //lots more lines of code a = addition(b,c); } int addition(int a, b) { return mysideaffect(a)+b; } }

y esto funcionaría, pero la segunda adición estaría equivocada al invocar el método. Se necesitarán más pruebas que no sean solo compilación.

He sido utilizado para hacer algunas refactorizaciones introduciendo errores de compilación. Por ejemplo, si quiero eliminar un campo de mi clase y convertirlo en un parámetro para algunos métodos, generalmente elimino el campo primero, lo que causa un error de compilación para la clase. Luego, introduciría el parámetro en mis métodos, lo que rompería las llamadas. Y así. Esto usualmente me dio una sensación de seguridad. Todavía no he leído ningún libro (todavía) sobre refactorización, pero solía pensar que esta es una forma relativamente segura de hacerlo. Pero me pregunto, ¿es realmente seguro? ¿O es una mala forma de hacer las cosas?


Es "seguro" en el sentido de que en un lenguaje suficientemente controlado en tiempo de compilación, te obliga a actualizar todas las referencias en vivo a lo que has cambiado.

Todavía puede salir mal si tiene un código compilado de manera condicional, por ejemplo, si ha utilizado el preprocesador C / C ++. Por lo tanto, asegúrese de reconstruir en todas las configuraciones posibles y en todas las plataformas, si corresponde.

No elimina la necesidad de probar sus cambios. Si ha agregado un parámetro a una función, entonces el compilador no puede decirle si el valor correcto fue el valor que proporcionó cuando actualizó cada sitio de llamada para esa función. Si eliminó un parámetro, aún puede cometer errores, por ejemplo, cambiar:

void foo(int a, int b);

a

void foo(int a);

Luego cambie la llamada desde:

foo(1,2);

a:

foo(2);

Eso compila bien, pero está mal.

Personalmente, utilizo los errores de compilación (y de enlace) como una forma de buscar código para referencias en vivo a la función que estoy cambiando. Pero debe tener en cuenta que esto es solo un dispositivo que ahorra trabajo. No garantiza que el código resultante sea correcto.


Es una forma bastante común de pensar, ya que encuentra todas las referencias a esa cosa específica. Sin embargo, los IDEs modernos como Visual Studio tienen la función Buscar todas las referencias que lo hace innecesario.

Sin embargo, hay algunas desventajas en este enfoque. Para proyectos grandes, puede llevar mucho tiempo compilar la aplicación. Además, no haga esto durante mucho tiempo (quiero decir, vuelva a poner las cosas en funcionamiento lo antes posible) y no haga esto para más de una cosa a la vez, ya que puede olvidar la forma correcta en que parcheó las cosas en la primera vez.


Es una forma y no puede haber una declaración definitiva sobre si es seguro o inseguro sin saber cómo se ve el código que está refabricando y las elecciones que realiza.

Si funciona para usted, entonces no hay razón para cambiar solo por el cambio, pero cuando tenga tiempo de leer, los recursos aquí pueden darle nuevas ideas que tal vez quiera explorar con el tiempo.

http://www.refactoring.com/


Esta es una técnica común y útil para los lenguajes compilados estáticamente. La versión general de lo que está haciendo se puede establecer de la siguiente manera:

Cuando realice un cambio en un módulo que podría invalidar algunos usos en los clientes de ese módulo, realice el cambio inicial de forma que se produzca un error en tiempo de compilación.

Hay una variedad de corolarios:

  • Si el significado de un método, función o procedimiento cambia, y el tipo tampoco cambia, cambie el nombre. (Cuando haya examinado y corregido todos los usos, probablemente cambie el nombre).

  • Si agrega un nuevo caso a un tipo de datos o un nuevo literal a una enumeración, cambie los nombres de todos los constructores de tipos de datos existentes o literales de enumeración. (O, si tiene la suerte de tener un compilador que comprueba si el análisis de caso es exhaustivo, hay formas más sencillas).

  • Si está trabajando en un idioma con sobrecarga, no solo cambie una variante o agregue una nueva variante. Te arriesgas a que la sobrecarga se resuelva en silencio de una manera diferente. Si usa la sobrecarga, es bastante difícil hacer que el compilador trabaje para usted de la manera que espera. La única forma que conozco para manejar la sobrecarga es razonar globalmente sobre todos los usos. Si su IDE no lo ayuda, debe cambiar los nombres de todas las variantes sobrecargadas. Desagradable.

Lo que realmente está haciendo es usar el compilador para ayudarlo a examinar todos los lugares en el código que podría necesitar cambiar.


Este es un enfoque común, pero los resultados pueden variar dependiendo de si su idioma es estático o dinámico.

En un lenguaje estático, este enfoque tiene sentido, ya que cualquier discrepancia que introduzca será capturada en tiempo de compilación. Sin embargo, los lenguajes dinámicos a menudo solo encontrarán estos problemas en el tiempo de ejecución. Estos problemas no serían captados por el compilador, sino más bien por su suite de pruebas; suponiendo que hayas escrito uno.

Me da la impresión de que estás trabajando con un lenguaje estático como C # o Java, así que continúa con este enfoque hasta que encuentres algún tipo de problema importante que diga lo contrario.


Esto suena similar a un método absolutamente estándar utilizado en el desarrollo basado en pruebas: escriba la prueba que hace referencia a una clase inexistente, de modo que el primer paso para hacer que pase la prueba sea agregar la clase, luego los métodos, y así sucesivamente. Vea el libro de Beck para ejemplos exhaustivos de Java.

Su método de refactorización parece peligroso porque no tiene ninguna prueba de seguridad (o al menos no menciona que tiene alguna). Puede crear código de compilación que realmente no hace lo que desea, o rompe otras partes de su aplicación.

Le sugiero que agregue una regla simple a su práctica: realice cambios que no sean de compilación solo en el código de prueba de la unidad . De esta forma, está seguro de tener al menos una prueba local para cada modificación, y está registrando la intención de la modificación en su prueba antes de realizarla.

Por cierto, Eclipse hace que este método "fail, stub, write" sea absurdamente fácil en Java: cada objeto inexistente está marcado para ti, y Ctrl-1 más una opción de menú le dice a Eclipse que escriba un stub (compilable) para ti. Me interesaría saber si otros idiomas y IDEs brindan un soporte similar.


Hago refactorizaciones habituales, pero sigo haciendo refactorizaciones introduciendo errores de compilación. Los hago generalmente cuando los cambios no son tan simples y cuando esta refactorización no es real refactorización (estoy cambiando la funcionalidad). Esos errores de compilación me están dando puntos que necesito echar un vistazo y hacer algunos cambios más complicados que el cambio de nombre o parámetro.


Me gustaría agregar a toda la sabiduría que hay aquí que hay un caso más donde esto podría no ser seguro. Reflexión. Esto afecta a entornos como .NET y Java (y también a otros, por supuesto). Su código se compilaría, pero todavía habría errores de tiempo de ejecución cuando Reflection intente acceder a variables inexistentes. Por ejemplo, esto podría ser bastante común si usa un ORM como Hibernate y olvida actualizar sus archivos XML de mapeo.

Puede ser un poco más seguro buscar en los archivos de códigos completos la variable específica / nombre del método. Por supuesto, podría generar muchos falsos positivos, por lo que no es una solución universal; y también podría usar la concatenación de cadenas en su reflejo, lo que también lo haría inútil. Pero al menos está un paso más cerca de la seguridad.

Sin embargo, no creo que haya un método 100% infalible, además de pasar por todo el código manualmente.


No veo ningún problema con eso. Es seguro, y siempre y cuando no esté cometiendo cambios antes de compilar, no tiene un efecto a largo plazo. Además, Resharper y VS tienen herramientas que hacen que el proceso sea un poco más fácil para usted.

Usa un proceso similar en la otra dirección en TDD: escribe código que puede no tener los métodos definidos, lo que hace que no se compile, luego escribe suficiente código para compilar (luego pasa pruebas, etc.)


Nunca me baso en una compilación simple al refactorizar, el código puede compilarse, pero es posible que se hayan introducido errores.

Creo que solo escribir algunas pruebas unitarias para los métodos o clases que desea refactorizar será lo mejor; luego, al ejecutar la prueba después de la refactorización, se asegurará de que no se introdujeron errores.

No digo ir al desarrollo impulsado por prueba, solo escribe las pruebas unitarias para obtener la confianza necesaria que necesitas para refactorizar.


Otra opción que podría considerar, si está utilizando uno de los lenguajes dotnet, es marcar el "viejo" método con el atributo Obsoleto, que introducirá todas las advertencias del compilador, pero aún así dejará el código invocable si hubiera código más allá de usted control (si está escribiendo una API, o si no usa la opción strict en VB.Net, por ejemplo). Puedes refactorizar alegremente, teniendo la versión obsoleta llamar a la nueva; como ejemplo:

public string Username { get { return this.userField; } set { this.userField = value; } } public int Login() { /* do stuff */ }

Se convierte en:

[ObsoleteAttribute()] public string Username { get { return this.userField; } set { this.userField = value; } } [ObsoleteAttribute("Replaced by Login(username, password)")] public int Login() { Login(Username, Pasword); } public int Login(string username, string password) { /* do stuff */ }

Así es como tiendo a hacerlo, de todos modos ...


Es bastante fácil pensar en ejemplos donde la refactorización por errores del compilador falla silenciosamente y produce resultados no deseados.
Algunos casos que me vienen a la mente: (asumiré que estamos hablando de C ++)

  • Cambio de argumentos a una función donde existen otras sobrecargas con parámetros predeterminados. Después de la refactorización, la mejor coincidencia para los argumentos puede no ser la esperada.
  • Clases que tienen operadores de molde o constructores de argumento único no explícitos. Cambiar, agregar, quitar o cambiar argumentos a cualquiera de estos puede cambiar las mejores coincidencias a las que se llama, dependiendo de la constelación involucrada.
  • Cambiar una función virtual y sin cambiar la clase base (o cambiar la clase base de manera diferente) dará lugar a que las llamadas se dirijan a la clase base.

confiar en los errores del compilador debe usarse solo si usted está absolutamente seguro de que el compilador captará cada cambio que se necesita realizar. Casi siempre tiendo a sospechar de esto.