¿Cómo se refactoriza una class que se edita constantemente?

Con el paso del time, mi equipo ha creado una class central que maneja una aglomeración de responsabilidades y se ejecuta en más de 8,000 líneas, todas escritas a mano, no autogeneradas.

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

Eso significa que muchas references que actualmente se leen así:

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

pronto leeré así:

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

La pregunta es: ¿cómo diablos coordinamos tal cambio? Las references a "orMonster" basura cada class de negocios. Algunos methods se llaman en literalmente miles de lugares en el código. Se garantiza que, cada vez que tengamos esa oportunidad, alguien más (probablemente varias personas más) en el equipo tendrá un código que llama a la propiedad .OpeningTime

¿Cómo se coordina un cambio de tan gran escala sin detener la productividad?

Debería hacer que el método antiguo llame al nuevo método. Luego, con el time, cambie las references al método anterior para llamar al nuevo método. Una vez que se cambian todas las references de los clientes, puede eliminar el método anterior.

Para get más información, vea Mover método en el clásico de Martin Fowler, Refactorización .

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

He manejado esto antes, procediendo y refacturando el código, pero luego agrego methods que coinciden con la firma anterior que reenvía las llamadas al nuevo método. Si agrega el atributo "Obsoleto" a estos methods temporales, su código seguirá generando tanto las llamadas al método antiguo como las nuevas llamadas a methods. Luego, con el time, 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 compilation para ayudarte a encontrar todo el código que necesita actualizarse.

No estoy seguro de qué idioma está usando, pero en .Net puede crear advertencias de compilation que le permitirán dejar las references antiguas durante un time para que funcionen como se esperaba, pero coloque una advertencia para que los vean otros desarrolladores.

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

Desarrolla tus cambios en una twig. Divida un subset de código en una nueva class, realice cambios en la base de clientes, realice testings exhaustivas y luego vuelva a fusionar.

Eso concentra la ruptura cuando se fusiona, no todo el ciclo de desarrollo.

Combina esto con la sugerencia de Patrick para que el monstruo llame a los pequeños monstruos . 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 methods 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 classs 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: escribir testings unitarias. Escribe muchas testings unitarias. Oh, chico, necesitas testings unitarias para sacar esto con security. ¿Mencioné que necesitas testings unitarias?

Mantenga el método anterior en su lugar y avance al nuevo método (como han dicho otros), pero también envíe un post de logging en el método de reenvío para recordar que debe eliminarlo.

Simplemente podría agregar un comentario, pero es muy fácil pasarlo por alto.

Sugiera usar una herramienta como nDepender para identificar todas las references a los methods de class. El resultado de nDepend se puede utilizar para darle una mejor idea sobre cómo agrupar los methods.

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

No estoy seguro de que dividirlo y simplemente hacer que porciones del mismo estén disponibles públicamente sea mejor. Eso está violando la ley del demeter y puede llevar al dolor de NullReference.

Sugeriría exponer al cronometrador a las 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.

No refactorizarlo.

Comience y siga la ley de Demeter. Crea una segunda class de monstruo y comienza desde cero. Cuando la segunda class 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 lugar de esto: "monster.TimeKeeper.OpeningTime.Subtract (DateTime.Now)"

Haz esto: monstruo.SubtractOpeningTime (DateTime.Now). No te mates con notación de puntos (de ahí el demeter)

Varias personas han proporcionado buenas respuestas sobre la orquestación del refactor en sí. Esa es la key. 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 key para hacerlo sin problemas es que cada persona debe hacer sus commits granulares y atómicos, y cada desarrollador debe hacer los commits de otras personas a menudo.

Primero lo veré usando classs parciales para dividir la class de monstruo único sobre muchos files, agrupando los methods en categorías.

Tendrás que evitar que cualquiera edite la class monstruo mientras te divides en los files.

A partir de ese momento, es probable que tengas less conflictos de fusión, ya que habrá less ediciones en cada file. A continuación, puede cambiar cada método en la class de monstruo (un método por checkin) para llamar a sus nuevas classs.

Una class 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 progtwigción de pares. Crea una twig para cada par de progtwigdores. 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 class de monstruo desde cero.

Intereting Posts