2008-12-12 18 views
11

Trabajo en un equipo de tamaño medio y me encuentro con estos archivos de clase dolorosamente grandes de forma regular. Mi primera tendencia es atacarlos con un cuchillo, pero eso generalmente empeora las cosas y me pone en un mal estado de ánimo.Tratando con objetos de Dios

Por ejemplo, imagine que acaba de recibir un servicio de Windows para trabajar. Ahora hay un error en este servicio y necesita averiguar qué hace el servicio antes de que pueda tener alguna esperanza de solucionarlo. Abre el servicio y ve que alguien decidió usar solo un archivo para todo. El método de inicio está ahí, el método Stop, los temporizadores, todo el manejo y la funcionalidad. Estoy hablando de miles de líneas de código. Los métodos bajo cien líneas de código son raros.

Ahora suponiendo que no puede volver a escribir toda la clase y que estas clases de dios van a seguir apareciendo, ¿cuál es la mejor manera de lidiar con ellas? ¿Dónde comienzas? ¿Qué intentas lograr primero? ¿Cómo lidias con este tipo de cosas y no solo quieres ponerte en aprietos?

Si tiene alguna estrategia para controlar su temperamento, también es bienvenido.

extremidades hasta el momento:

  1. Establecer cobertura de la prueba
  2. plegado de código
  3. Reorganizar los métodos existentes comportamiento
  4. Documento como descubrió
  5. proponerse la mejora incremental de

Edición:

Charles Conway recomienda un podcast que resultó ser muy útil. link

Michael Feathers (hombre en el podcast) comienza con la premisa de que tenían mucho miedo de simplemente sacar un proyecto del control de la fuente y simplemente jugar con él directamente y luego descartar los cambios. Puedo decir que soy culpable de esto.

Se dice esencialmente que debe tomar el elemento sobre el que desea obtener más información y comenzar a separarlo. Descubre sus dependencias y luego descárgalas. Síguelo a donde quiera que vaya.

Gran punta Tome la clase grande que se usa en otros lugares y haga que implemente una interfaz emtpy. Luego tome el código usando la clase y haga que cree una instancia de la interfaz. Esto le dará una lista completa de todas las dependencias de esa clase grande en su código.

+3

Ejecutar, y no te detengas a mirar atrás! – JoshBerke

+0

Si solo pudiera Josh ... Si tan solo pudiera ... –

+0

Heh, sí, una vez tuve que revisar esta aplicación COM + de subprocesos múltiples, 20,000 líneas de código, tenía una complejidad cíclica de magnitudes más altas que nunca. visto. Ohh y solo fue como 5 métodos. Yo vomité y me reí de Mgmt por sugerir que "reutilizáramos" este servicio. – JoshBerke

Respuesta

9

¡Ouch! Suena como el lugar donde uso para trabajar.

Eche un vistazo a Working effectivly with legacy code. Tiene algunas gemas sobre cómo lidiar con un código atroz.

DotNetRocks hizo recientemente un show al trabajar con el código heredado. No hay una píldora mágica que la haga funcionar.

El mejor consejo que he escuchado es comenzar a ajustar gradualmente el código en las pruebas.

+0

Hubo algunos muy buenos consejos en ese podcast. Gracias por la publicacion. –

+0

En cualquier momento, espero que pueda encontrar una estrategia de trabajo. Si encuentra algún método en particular que lo ayude, ¿puede publicarlo? Me encantaría escuchar al respecto. –

+0

Voy a hacer justamente eso. –

4

Eso me recuerda mi trabajo actual y cuando me uní por primera vez. No me permitieron volver a escribir nada porque tenía el mismo argumento: "¡Estas clases son tan grandes y están mal escritas! Nadie podría entenderlas y mucho menos agregarles nuevas funcionalidades".

Así que lo primero que haría es asegurarme de que haya pruebas exhaustivas detrás de las áreas que desea cambiar. Y al menos entonces tendrás la oportunidad de cambiar el código y no tener (demasiados) argumentos (con suerte). Y mediante pruebas, me refiero a probar los componentes funcionalmente con pruebas de integración o aceptación y asegurarme de que esté 100% cubierto.Si las pruebas son buenas, entonces debería poder cambiar el código con confianza dividiendo la clase grande en otras más pequeñas, eliminando la duplicación, etc.

3

El código plegable puede ayudar. Si puede mover cosas dentro de la clase gigante y organizarlo de una manera un tanto lógica, entonces puede colocar pliegues alrededor de varios bloques.

Oculte todo, y vuelve a un paradigma C, excepto con pliegues en lugar de archivos separados.

+0

Muchos de los códigos base son bastante procesales, por lo que a las soluciones a este problema que se manejaron en C también podrían ser un buen lugar. ¡Gracias! –

+0

Suena como "poner lápiz labial en un cerdo" - Mordería la bala y trataría de mejorar el diseño gradualmente. – tvanfosson

+0

Creo que ese es mi mayor bloqueo mental. Una vez que comienzo por el camino de lo que "debería" estar haciendo, llego a un estado mental en el que la única solución que veo es una reescritura casi completa. –

4

Incluso si no puede refactorizar el archivo, intente reorganizarlo. Mueva métodos/funciones para que estén organizados al menos lógicamente dentro del archivo. Luego ponga muchos comentarios explicando cada sección. No, no ha reescrito el programa, pero al menos ahora puede leerlo correctamente, y la próxima vez que tenga que trabajar en el archivo, tendrá muchos comentarios escritos por usted (lo que significa que lo hará). ser capaz de entenderlos) que lo ayudará a manejar el programa.

+0

Si el código está tan mal escrito como para necesitar "muchos comentarios", definitivamente necesita ser refactorizado. El código debe explicarse solo con comentarios mínimos. – tvanfosson

+0

El código puede estar "bien escrito", pero debido a que hace algo complejo, para alguien que nunca lo haya visto antes, necesita muchos comentarios. No son inútiles (por ejemplo, recorre todos los objetos de la lista) pero son útiles (por ejemplo, es necesario restablecer foo() para garantizar que bar() no falle más adelante). – Elie

+0

Esto es más útil de lo que podría sonar para los puristas: he * escrito * código que tenía que hacer horribles giros de procedimiento porque eso era lo que tenía que hacer (era leer y enrutar selectivamente el correo electrónico). Lo mejor que pude dejar para el próximo programador fue un montón de comentarios. – staticsan

2

Lo primero que haría es escribir algunas pruebas unitarias para boxear el comportamiento actual, suponiendo que ya no existan. Luego comenzaría en el área donde tengo que hacer el cambio y trataré de limpiar ese método, es decir, refactorizar el código de trabajo antes de introducir cambios. Use técnicas comunes de refactorización para extraer y reutilizar métodos de métodos largos existentes para hacerlos más comprensibles. Cuando extrae un método, busque otros lugares en el código donde exista un código similar, ponga esa área en el casillero y reutilice el método que acaba de extraer.

Busque grupos de métodos que "se junten" y se dividan en sus propias clases. Escriba algunas pruebas de cómo deberían funcionar esas clases, construya las clases usando el código existente como una plantilla si es necesario, luego sustituya las nuevas clases en el código existente, eliminando los métodos que reemplazan. Una vez más, usa tus pruebas para asegurarte de que no estás rompiendo nada.

Mejore lo suficiente el código existente para que sienta que puede implementar su nueva característica/solución de una manera limpia. Luego, escriba las pruebas para la nueva característica/solución e implemente para pasar las pruebas. No sientas que tienes que arreglar todo la primera vez. Intente una mejora gradual, pero siempre deje el código mejor de lo que lo encontró.

3

Me he encontrado con esta situación también.

Personalmente imprimo (sí, puede ser un montón de páginas) el código primero. Luego dibujo un recuadro alrededor de secciones de código que no forman parte de ningún "bucle principal" o que son solo funciones auxiliares y me aseguro de que entiendo estas cosas primero. La razón es que probablemente se mencionen muchas veces en el cuerpo principal de la clase y es bueno saber lo que hacen

En segundo lugar, identifico los algoritmos principales y los descompongo en sus partes usando un sistema de numeración que alterna entre números y letras (es feo pero funciona bien para mí). Por ejemplo, podrías estar mirando parte de un algoritmo 4 "niveles" profundos y la numeración sería 1.b.3.e o alguna otra cosa horrible.Tenga en cuenta que cuando digo niveles, no me refiero directamente a los bloques de control o al ámbito necesariamente, pero donde he identificado los pasos y subpasos de un algoritmo.

Luego, solo se trata de leer y volver a leer el algoritmo. Cuando comienzas parece mucho tiempo, pero creo que al hacerlo desarrolla una capacidad natural para comprender una gran cantidad de lógica a la vez. Además, si descubres un error atribuido a este código, al haberlo desglosado visualmente en papel antes de tiempo, te ayudará a "navegar" el código más adelante, ya que ya tienes una especie de mapa en tu cabeza.

Si sus jefes no creen que usted entienda algo hasta que tenga algún tipo de UML que lo describa, un diagrama de secuencia UML podría ayudar aquí si pretende que los niveles del subpaso son diferentes "clases" representadas horizontalmente, y comienzan to-finish se representa verticalmente de arriba a abajo.

+0

¡Ooooh! Eso es mucho esfuerzo, pero esta debe ser la manera perfecta de hacerlo. Prestigio. –

3

Siento tu dolor. Abordé algo así una vez para un proyecto de hobby que implica el procesamiento de datos de TV digital en mi computadora. Un compañero en un foro de hardware había escrito una herramienta increíble para grabar programas, ver todo lo que estaba encendido y más. Además, él había hecho un trabajo increíblemente vital para resolver los errores en las señales de transmisión real que violaban el estándar. Había hecho un trabajo increíble con la programación de hilos para asegurarse de que no importaba qué pasara, no perdería esos paquetes en tiempo real: en un viejo Pentium, podía grabar cuatro transmisiones simultáneamente mientras jugaba Doom y nunca perdía un paquete. En resumen, este código incorporó una tonelada de gran conocimiento. Esperaba tomar algunas piezas e incorporarlas a mi propio proyecto.

Recibí el código fuente. Un archivo, 22,000 líneas de C, sin abstracción. Pasé horas leyéndolo; hubo todo este gran trabajo, pero todo fue hecho mal. No pude reutilizar ni una sola línea, ni siquiera una sola idea.

No estoy seguro de cuál es la moraleja de la historia, pero si me hubiera visto obligado a utilizar esto en el trabajo, hubiera pedido permiso para piezas de una en una, pruebas de unidades de construcción para cada pieza, y eventualmente crezca una cosa nueva y sensata fuera de las piezas. Este enfoque es un poco diferente a tratar de refactorizar y mantener un ladrillo grande en su lugar, pero preferiría haber dejado intacto el código heredado y tratar de abrir un nuevo sistema en paralelo.

+0

Algunas veces, las pruebas unitarias pueden ser una buena herramienta de diagnóstico para averiguar qué es lo que realmente está haciendo ese método llamado "Método1" :). –

Cuestiones relacionadas