2009-02-24 9 views
10

A lo largo del tiempo, mi equipo ha creado una clase central que maneja una aglomeración de responsabilidades y ejecuta más de 8,000 líneas, todas escritas a mano, no generadas automáticamente.¿Cómo se refactoriza una clase que se edita constantemente?

El mandato se ha reducido. Necesitamos refactorizar la clase monstruo. La mayor parte del plan es definir categorías de funcionalidad en sus propias clases con una relación has-a con la clase monstruo.

Eso significa que una gran cantidad de referencias que actualmente lee así:

var monster = new orMonster(); 
var timeToOpen = monster.OpeningTime.Subtract(DateTime.Now); 

pronto lea así:

var monster = new Monster(); 
var timeToOpen = monster.TimeKeeper.OpeningTime.Subtract(DateTime.Now); 

La pregunta es: ¿Cómo diablos Coordinamos un cambio tan ? Las referencias a "orMonster" basura cada clase de negocios. Algunos métodos se llaman en literalmente miles de lugares en el código. Está garantizado que, cada vez que tengamos esa oportunidad, alguien más (probablemente varias personas más) en el equipo tendrá un código que llame a la propiedad .OpeningTime

¿Cómo se coordina un cambio a gran escala sin la productividad de molienda a un alto?

+0

Con Visual Studio, haga clic con el botón derecho en "Buscar todas las referencias ..." y reemplace. – core

Respuesta

27

Debe hacer que el método anterior llame al nuevo método. Luego, con el tiempo, cambie las referencias al método anterior para llamar al nuevo método. Una vez que se cambian todas las referencias de los clientes, puede eliminar el método anterior.

Para obtener más información, consulte Move Method en el clásico de Martin Fowler, Refactoring.

+0

Esto. Y márquelos en desuso o lo que sea, si su idioma tiene esa característica. Seguirán funcionando, pero obtendrán subrayados/tachaduras desagradables en el IDE y llenará la consola de advertencias. –

10

Una cosa que puede hacer es dejar temporalmente los métodos proxy en la clase monstruo que delegará en el nuevo método. Después de una semana más o menos, una vez que esté seguro de que todo el código está usando el nuevo método, puede eliminar el proxy de forma segura.

1

No lo refactorice.

Comience y siga la ley del demeter. Crea una segunda clase de monstruo y comienza desde cero. Cuando la segunda clase de monstruo finalice y funcione, reemplaza las ocurrencias del primer monstruo. Intercambiarlo. Con suerte comparten una interfaz, o puede hacer que eso suceda.

Y en vez de esto: "monster.TimeKeeper.OpeningTime.Subtract (DateTime.Now)"

hacer esto: monster.SubtractOpeningTime (DateTime.Now). No te mates con notación de punto (de ahí el demeter)

2

Sugiere usar una herramienta como nDepender para identificar todas las referencias a los métodos de clase. El resultado de nDepend se puede utilizar para darle una mejor idea sobre cómo agrupar los métodos.

7

He manejado esto antes procediendo y refacturando el código, pero luego agrego métodos que coinciden con la firma anterior que reenvía las llamadas al nuevo método. Si agrega el atributo "Obsoleto" a estos métodos temporales, su código seguirá generando tanto las llamadas al método antiguo como las nuevas llamadas a métodos. Luego, con el tiempo, puede volver atrás y actualizar el código que está llamando al método anterior. La diferencia aquí es que obtendrás "Advertencias" durante la compilación para ayudarte a encontrar todo el código que necesita actualizarse.

6

No estoy seguro del idioma que está utilizando, pero sí.Net puede crear advertencias de compilación que le permitirán dejar las referencias antiguas durante un tiempo para que funcionen como se esperaba, pero coloque una advertencia para que los vean otros desarrolladores.

http://dotnettipoftheday.org/tips/ObsoleteAttribute.aspx

+0

Awesome tip. Desearía poder votar esto dos veces. –

+0

Al aceptar esto, la respuesta es 15 puntos, ¿no es así? ¡Casi igual de bueno! ;) –

3

Mantener el método de edad en su lugar y con interés el nuevo método (como han dicho otros), sino también enviar un mensaje de registro en el método de reenvío de recordar que debe quitarlo.

Puede agregar un comentario pero es demasiado fácil de perder.

1

Varias personas han proporcionado buenas respuestas sobre la orquestación del refactor en sí. Esa es la clave. Pero también preguntaste sobre la coordinación de los cambios entre varias personas (que creo que fue el quid de la cuestión). ¿Qué control de fuente estás usando? Cualquier cosa como CVS, SVN, etc. puede manejar los cambios entrantes de múltiples desarrolladores a la vez. La clave para hacerlo sin problemas es que cada persona debe hacer sus commits granulares y atómicos, y cada desarrollador debe hacer los commit de otras personas a menudo.

2
var monster = new Monster(); 
var timeToOpen = monster.TimeKeeper.OpeningTime.Subtract(DateTime.Now); 

No estoy seguro de que dividirlo y simplemente hacer porciones de él públicamente disponibles sea mejor. Eso está violando la ley del demeter y puede generar dolor de referencia nula.

Sugeriría exponer timekeeper a personas sin involucrar al monstruo.

En todo caso, sería bueno analizar el API y ver qué se puede cortar y encapsular dentro del monstruo. Ciertamente, dar juguetes monstruosos para jugar, en lugar de hacer que un monstruo haga todo el trabajo en sí, es una buena decisión. El esfuerzo principal es definir los juguetes que el monstruo necesita para simplificar su trabajo.

+0

Por otro lado, este podría ser un punto de partida: obtenerlo dividido funcionalmente y luego eliminar la clase de monstruo en una fecha posterior. – TofuBeer

+0

Sí, pero todos sabemos que una "fecha posterior" generalmente significa nunca o más de un año después. Es mejor tomar la sugerencia de Chris Holme, crear un conjunto de pruebas unitarias y comenzar de nuevo y copiar el código pieza por pieza en la nueva estructura. – Quibblesome

4

Desarrolla tus cambios en una rama. Divida un subconjunto de código en una nueva clase, realice cambios en la base de clientes, realice pruebas exhaustivas y luego vuelva a fusionar.

Que concentra la rotura en cuando se fusiona, no en todo el ciclo de desarrollo.

Combine esto con la sugerencia de Patrick al have the monster call the small monsters. Eso te permitirá revertir fácilmente si tu código de cliente fusionado rompe los cambios en ese cliente. Como dice Patrick, podrás eliminar los métodos del monstruo (ahora colillas) una vez que pruebes que nadie lo está usando.

También me hago eco de los consejos de varios carteles para exponer las clases rotas directamente, no a través del monstruo. ¿Por qué aplicar solo la mitad de una cura? Con el mismo esfuerzo, puede aplicar una cura completa.

Finalmente: pruebas de unidades de escritura. Escribe muchas pruebas unitarias. Oh, chico, necesitas pruebas unitarias para sacar esto con seguridad. ¿Mencioné que necesitas pruebas unitarias?

+0

+1 Mis pensamientos exactamente. – si618

-2

Una clase tan grande es realmente un problema. Dado que creció tanto y nadie se sintió incómodo, debe haber algún problema con las políticas del proyecto. Yo diría que deberías dividirte en pares y hacer programación de pares. Crea una rama para cada par de programadores. Trabaja por 1-2 días en la refactorización. Compara tus resultados. Esto te ayudará a evitar la situación cuando la refactorización iría desde el principio en la dirección incorrecta y, finalmente, eso llevaría a la necesidad de reescribir la clase de monstruo desde cero.

1

Al principio usaré la clase parcial para dividir la clase de monstruo único sobre muchos archivos, agrupando los métodos en categorías.

Deberá evitar que cualquiera edite la clase monstruo mientras se divide en los archivos.

A partir de ese momento, es probable que obtenga menos conflictos de fusión, ya que habrá menos ediciones en cada archivo. A continuación, puede cambiar cada método en la clase de monstruo (un método por checkin) para llamar a sus nuevas clases.

Cuestiones relacionadas