2009-02-09 8 views
6

Me han encargado la tarea de mantener y refactorizar un sistema Java heredado. Actualmente hago C# y .NET, aunque estoy familiarizado con Java.Aprendiendo un sistema heredado de Java

El sistema heredado usa RMI, una arquitectura cliente/servidor y fue diseñado para la JVM 1.4. Está usando para la interfaz de usuario (hasta donde puedo decir), Swing y AWT.

Mi pregunta es esta: ¿Cuál es la mejor manera de aceptar la base de código que me acaban de entregar? Estoy pensando en diagramas de flujo de pantallas, definiendo los límites entre las llamadas de RMI y las pruebas de unidades de escritura (para los bits que se pueden probar).

¿Qué haces en la situación cuando te entregan una base de código desconocida?

Gracias!

-Jarrod

Respuesta

5

La primera cosa que hago con cualquier nuevo código estoy sola mano es mirar a las pruebas unitarias existentes. Escribir pruebas nuevas suele ser lo segundo.

+0

No estoy seguro si eso es así, pero me parece gracioso que lo siguiente sea escribir nuevas pruebas (no dice mucho sobre las pruebas de unidades existentes, ¿o sí? :) – Learning

+0

No me refiero a menospreciar a mis colegas , pero aún no he visto un conjunto 100% completo de pruebas unitarias (incluso las mías se pueden mejorar). :) Además, considero que escribir pruebas es solo una mejor manera de aprender que leerlas. –

+0

ROTFL, buena :))))))) – IAdapter

0

Java 1.4 con RMI no es heredado, ¡eso es prácticamente nuevo según algunos estándares!

Haz lo que puede hacer para familiarizarse con el lugar donde las cosas son - pruebas de unidad, el rastreo de código/llamadas, trabajando algunos diagramas UML, etc.

Trate de comenzar con una sección reducida de la misma y el rastreo de las rutas de código para familiarizarse con el lugar en el que se encuentra o asignarle algunas pequeñas correcciones/mejoras que requieren revisar el código.

+0

Java 1.4 es bastante antigua para los estándares Java (7 años desde su lanzamiento). Sun declaró "End of Service Life" (EOSL) por 1,4 en octubre después de 2 años en la transición al final de su vida útil. Java 5 llega a EOSL a finales de este año (http://java.sun.com/products/archive/eol.policy.html). –

+0

En cualquier caso, el término heredado simplemente significaba que su empresa ya no usaba el sistema o ya no empleaba a los desarrolladores originales, por lo que el código es "viejo" independientemente de los sentimientos de la comunidad en Java 1.4 – Karl

+0

Correcto, "prácticamente nuevo" fue en su mayoría irónica, pero Java 5 acercándose a su EOL no significa que 1.4 todavía no es común. A pesar de todo, Karl tiene razón sobre el significado real del término "legado". –

4

Depende mucho de la calidad de la base de código. En realidad, podría definirlo aún más estrictamente: depende de qué tan claro sea el código y de qué tan bien comentado esté (y lo escribo como alguien cuya ubicación en varias ocasiones heredó bases de código heredadas mal documentadas).

Cuanto peor es la base de código, más necesitará inferir su funcionamiento desde el exterior; si no puede descubrir qué hace una función, al menos podrá averiguar qué La GUI parece implicar que está funcionando. Tener una interfaz RMI puede ser una bendición, ya que te brinda una imagen simplificada de lo que la GUI necesita ver: es una muy buena idea crear un resumen de la interfaz remota.

Si la base de código es realmente pobre, es probable que las pruebas de la unidad no lo ayuden, ya que puede estar probando algo que está mal incluso si se implementa correctamente. Un ejemplo de lo último que he heredado: (nombres y descripciones extrae intencionalmente - que no eran útiles en el original, en todo caso)

public static int[] a (List<int[]> input){ 

    ... lots of opaque rubbish 
    return b(input); 
} 

public static List<int[]> b{ List<int[]> input) 
{ 
    ... more crap.... 
    return some horribly mangled list of int[]; 
} 

Al final resultó que, lo un logró fue para ordenar la matrices por el valor de su segundo elemento. Probar b comportarse adecuadamente sería inútil en este caso.

sugerencias para ayudar a conservar la cordura:

  • Quitar código que no está claro en uso.
  • Trate de factorizar los "números mágicos"
  • busque cualquier archivo de hardcode, ruta, o referencias de recursos y factorítelos en propiedades, o cualquier otra manera central y genérica para tratar con ellos.
  • intenta averiguar si la aplicación se está desempeñando realmente de la manera que se supone que es - no es inusual que una función realmente complicada que no puede hacer cabeza o cola realmente represente algo que los usuarios "nunca funcionó bien" ".
  • Encuentra cualquier documentación original o especificaciones de cuando se diseñó por primera vez.
  • Como mencionas en 1.4, los mapas genéricos pueden aclarar qué tipos de objetos se pasan. Si tiene un objeto respaldado por un HashMap, y cómo se está poblando es un misterio, realmente puede simplificar su vida para determinar que en realidad es un Mapa de cadenas a enteros, que a menudo es mucho más fácil de entender de lo que se supone. estar haciendo.

Por supuesto, la mayor parte de esto es innecesario si la base de código está bien escrita, pero entonces su trabajo será mucho más fácil en cualquier caso. Y buena suerte.

1

Lo primero que hago cuando recibo un código nuevo es "¡intente hacerlo funcionar!" Con esto quiero decir:

  • instala por primera vez (como en la instalación notas si lo hay)

  • continuación familiarizarme con ella (leyendo el manual de usuario y la ejecución de algunos importantes casos de uso)

  • luego un poco de ingeniería inversa para descubrir las capas principales. clases y dependencias

  • entonces puedo pensar en escribir nuevos casos de prueba (al nivel de aceptación primero, que serán de utilidad para las pruebas de regresión más adelante)

  • entonces me dan a mí mismo algunos "desafíos" sobre la adición de esta característica (incluso si no es necesario) o mejorar el rendimiento de esa función existente: esa es una buena manera de profundizar en la base de código existente

  • por supuesto, si hay algunos errores pendientes conocidos/RFE, voy a trabajar en estos primeros

En su caso específico, también trataría de documentar las llamadas de RMI, qué llamada o secuencia de llamadas para hacer qué, y relacionarla con las características de nivel de uso cuando sea posible.

Además (y eso debe ser lo primero en realidad), una cosa importante a saber son los principales objetivos de este sistema (¿por qué tiene este cliente este sistema?). Conocer los objetivos y tenerlos en cuenta evitará que te alejes de estos objetivos mientras mantienes el código.

0

Compilarlo y ejecutarlo sería lo primero. Comience a tener una idea del problema que estaba tratando de resolver.

Tal vez puedas importarlo en una herramienta UML como JUDE y obtener una imagen de cómo interactúan las clases.

Escribir pruebas JUnit es una sugerencia excelente. Me gustaría ver qué capas tenía la aplicación. Si es difícil de probar, quizás esté demasiado acoplado. Las pruebas unitarias te dirán eso. También te darán una red de seguridad si decides que alguna refactorización está en orden.

JDK 1.4? Ya pasó el final de su vida de soporte. También me gustaría ver si el código se compilará y se ejecutará bajo JDK 5 como mínimo, preferiblemente JDK 6. Tal vez podrías incluir algunas de esas pruebas JUnit en JMeter y hacer una prueba rápida de carga de un pobre hombre con 5 usuarios simultáneos.

Si tiene un modelo de datos, hágalo en ERWin y comience a ver cómo las tablas, los objetos y las pantallas fluyen juntas.

2

Una cosa que me ayuda a trabajar con código que es nuevo para mí, esto es mucho menos necesario para un código bien escrito, es refactorizarlo masivamente por un día o dos y luego descartar todos mis cambios. Este proceso me ayuda a entender lo que hace el código; trabajar con código me ayuda a entenderlo. También comienza a enseñarme qué partes del código son frágiles.

Si tiene la oportunidad de migrar a una versión más nueva de Java, entonces la genérica de todas las colecciones le ayudará a comprender qué tipos de datos se pasan.

Por supuesto, hago esto después de instalar el software en un laboratorio de pruebas y jugar con él un poco para entender lo que hace.

Editar: Pensando en mi respuesta, también es útil habilitar todos los rastreos y registros de diagnóstico, usar el sistema y luego estudiar los registros. Si existe un rastreo de protocolo de comunicación, entonces observar este seguimiento ayudará a comprender el protocolo de comunicación utilizado por el código, quizás con un rastro de Wireshark de la misma prueba.

Otra migración útil es migrar de la biblioteca de Concurrencia anterior a la nueva biblioteca de simultaneidad de Java 5 (y 6). Esto lo ayudará a comprender dónde están los hilos, cuándo se inician y cuándo se apagarán.

Por supuesto, con cualquier cambio de código en una base de código desconocida, supongo que se realizan las pruebas adecuadas para garantizar que no haya nada roto. Sin embargo, para equilibrar esto, aprendí que después de refactorizar el código mal escrito, los errores recientemente introducidos son a menudo mucho más fáciles de encontrar que los errores que existían antes de la refactorización.

0

Por supuesto, podría intentarlo. Es lento pero un día dedicado a recorrer el código podría salvar una semana de búsqueda de errores más adelante ...

También podría intentar el enfoque de búsqueda del tesoro, hacer una lista de todas las características e ir a buscar el código ...

0

Primero revise el código para comprender su estructura. Luego, ejecute la aplicación en modo de depuración y repárela un par de veces. Esto te mostrará el flujo y la estructura.

Utilice Netbeans para realizar diagramas de clase de ingeniería inversa.

1

Hubo una charla a la que asistí Mike Hill en la que habló sobre un proceso que usa para lidiar con el código heredado. Lo llamó "microtesting" y básicamente está aislando cada cosa que quiere probar (en su propia función u objeto pequeño) y escribiendo pruebas a su alrededor. Estas pruebas no significan hacer valer las cosas en el sentido comercial: simplemente están destinadas a capturar el estado que queda el sistema después de que se ejecutan las líneas bajo prueba. Por lo general, afirmaba que las variables tenían los valores que tenían en el depurador. Naturalmente, estas pruebas pasarían inicialmente, pero después de haber escrito suficientes de ellas, tendría una instantánea del sistema.

Una vez que esas pruebas estaban en su lugar, él podría empezar a refactorizar ese código para intentar darle sentido a lo que estaba tratando de hacer. Podría hacerlo con seguridad porque sus "microtests" fallarían si cambiara la forma en que la función (u objeto) se comportaba aunque fuera levemente.Si bien eso significaba que no podía hacer grandes cambios de diseño, podía eliminar un montón de ruido y podía hacerse una idea de lo que estaba haciendo el sistema. Una vez que tenía una idea clara de lo que el código estaba logrando, podía comenzar a mejorarlo, encontrar errores, etc.

No hay muchos recursos gratuitos en esto, por eso no proporciono enlaces. Espero haber logrado describir lo suficiente como para darte algunas ideas.

1

La experiencia me ha demostrado que hay 3 objetivos principales que tiene al aprender un sistema heredado.

  • Aprender lo que se supone que el código para hacer
  • aprender cómo se hace de ellos
  • (crucialmente) Aprende por qué se les hace la manera que lo hace

Estas tres piezas son muy importante, y hay algunos trucos para ayudarlo a comenzar.

En primer lugar, resista la tentación de simplemente presionar ctrl-click (o lo que sea que use su IDE) a su manera alrededor del código para comprender todo. Probablemente no puedas mantener todo en perspectiva en tu mente de esta manera, especialmente cuando cada línea te obliga a mirar muchas otras clases para entender de qué se trata.

Lea la documentación cuando sea posible; generalmente te ayuda a obtener rápidamente un marco mental sobre el cual construir todo lo que sigue.

Ejecute casos de prueba cuando sea posible.

No tenga miedo de preguntarle a alguien que sabe si tiene alguna pregunta. De acuerdo, no debe perder el tiempo de otros empleados con consultas inane, pero si hay algo que simplemente no entiende (esto es especialmente cierto con preguntas más conceptuales como, "¿No tendría mucho más sentido implementar esto como un ___ "o algo así), es probable que valga la pena buscar la respuesta antes de meterse en un lío y no saber por qué.

Cuando finalmente se acostumbre a leer el código, comience en un lugar lógico "principal" e ir desde allí. No solo lea el código de arriba a abajo, o en orden alfabético, o cualquier cosa (esto es probablemente obvio).