2009-02-09 4 views
6

Sono stato incaricato di mantenere e refactoring un sistema Java legacy. Attualmente faccio C# e .NET, anche se ho familiarità con Java.Apprendimento di un sistema Java legacy

Il sistema legacy utilizza RMI, un'architettura client/server ed è stato progettato per la JVM 1.4. Sta usando per l'interfaccia utente (per quanto posso dire), Swing e AWT.

La mia domanda è questa: qual è il modo migliore per venire a patti con il codebase che ho appena ricevuto? Sto pensando ai diagrammi di flusso degli schermi, definendo i confini tra le chiamate RMI e scrivendo i test delle unità (per i bit testabili).

Cosa fai nella situazione quando ti viene consegnata una base di codice non familiare?

Grazie!

-Jarrod

risposta

5

La prima cosa che faccio con qualsiasi nuovo codice Sono sola mano è guardare i test di unità esistenti. Scrivere nuovi test di solito è la seconda cosa.

+0

Non sono sicuro se ciò è inteso, ma trovo divertente che la prossima cosa sia di solito scrivere nuovi test (non dirò molto sui test delle unità esistenti, vero? :) – Learning

+0

Non intendo denigrare i miei colleghi , ma non ho ancora visto un set completo di test unitari al 100% (anche il mio può essere migliorato). :) Inoltre, trovo che i test di scrittura sono solo un modo migliore per imparare piuttosto che leggerli. –

+0

ROTFL, ottimo :))))))) – IAdapter

0

Java 1.4 con RMI non è legacy, è praticamente nuovo da alcuni standard!

fare tutto il possibile fare per familiarizzare con cui le cose sono - test di unità, tracciando codice/chiamate, lavorando su alcuni diagrammi UML, ecc

Prova a iniziare con una sezione più piccola di esso e tracciare percorsi di codice per familiarizzare con dove sono le cose o assegnarsi alcune piccole correzioni/miglioramenti che richiedono la ricerca attraverso il codice.

+0

Java 1.4 è piuttosto vecchio per gli standard Java (7 anni dalla versione). Sun ha dichiarato "End of Service Life" (EOSL) per 1,4 in ottobre dopo 2 anni in transizione di fine vita. Java 5 raggiunge EOSL entro la fine dell'anno (http://java.sun.com/products/archive/eol.policy.html). –

+0

In ogni caso, il termine legacy qui significava semplicemente che la sua azienda non utilizzava più il sistema o non impiegava più gli sviluppatori originali, quindi il codice è "vecchio" a prescindere dai sentimenti della comunità su Java 1.4 – Karl

+0

Giusto, "praticamente nuovo" era per lo più ironico, ma Java 5 vicino al suo EOL non significa che 1.4 non è ancora comune. Indipendentemente da ciò, Karl ha ragione sul vero significato del termine "legacy". –

4

Dipende molto dalla qualità del codice base. In realtà, potresti definirlo in modo ancora più ristretto: dipende da quanto è chiaro il codice e quanto bene è commentato (e lo scrivo come qualcuno che è stato ripetutamente nella posizione di ereditare codebase legacy scarsamente documentate).

Peggio il codice base, più dovrai inferirne il funzionamento dall'esterno - se non riesci a capire cosa sta facendo una funzione, potresti almeno essere in grado di capire cosa La GUI sembra implicare che stia facendo. Avere un'interfaccia RMI può essere una benedizione in quanto fornisce un'immagine semplificata di ciò che la GUI deve vedere: l'astrazione dall'interfaccia remota è un'ottima idea.

Se il codebase è veramente scadente, non è probabile che i test di unità ti aiutino, poiché potresti provare qualcosa di sbagliato anche se correttamente implementato. Un esempio dal l'ultima cosa che ho ereditato: (nomi e le descrizioni intenzionalmente rimossa - non sono stati utili per l'originale in ogni 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[]; 
} 

Come si è scoperto, che cosa un compiuto è stato quello di ordinare la matrici dal valore del loro secondo elemento. Testare b comportarsi correttamente sarebbe inutile in questo caso.

Suggerimenti per aiutarti a mantenere la vostra sanità mentale:

  • codice di rimozione che non è chiaramente in uso.
  • Cerca di calcolare tutti i "numeri magici"
  • trova qualsiasi file di codice, percorso o riferimenti di risorse e li tratti in proprietà o in qualsiasi altro modo generico centrale per gestirli.
  • cercano di accertare se l'applicazione è molto performante il modo in cui si suppone che - non è insolito per qualche funzione davvero complicato che non si può fare testa o coda di realtà rappresentare qualcosa che gli utenti sono sentiti "mai veramente funzionato a destra ".
  • Trova qualsiasi documentazione originale o specifiche da quando la cosa è stata progettata per la prima volta.
  • Dato che si menziona 1.4, la generizzazione delle mappe può chiarire quali tipi di oggetti vengono superati. se hai un oggetto supportato da una HashMap, e il modo in cui viene popolato è un mistero, può davvero semplificarti la vita per determinare che si tratta in realtà di una mappa di stringhe per numeri interi - che è spesso molto più facile da capire rispetto a ciò che è effettivamente supposto fare.

Naturalmente la maggior parte di questo non è necessario se il codice base è ben scritto, ma in questo caso il lavoro sarà molto più facile in ogni caso. E buona fortuna.

1

La prima cosa che faccio quando si riceve un nuovo codice è "cercare di farlo funzionare"! Con questo voglio dire:

  • prima installarlo (come per l'installazione note se presente)

  • poi mi familiarizzare con esso (con la lettura del manuale d'uso e l'esecuzione di alcuni importanti casi d'uso)

  • quindi un po 'di reverse engineering per scoprire i livelli principali. classi e le dipendenze

  • poi mi viene in mente di scrivere nuovi casi di test (a livello di accettazione prima, che saranno utili per i test di regressione in seguito)

  • poi mi danno alcune "sfide" circa l'aggiunta di questa funzionalità (anche se non è necessario) o migliorare le prestazioni di quella funzione esistente: è un buon modo per scavare nella base di codice esistente

  • ovviamente, se ci sono alcuni bug/RFE in attesa, lavorerò su questi primi

Nel tuo caso specifico, vorrei anche provare a documentare le chiamate RMI, quale chiamata o sequenza di chiamate per fare cosa, e collegarlo alle funzionalità di livello d'uso quando possibile.

Inoltre (e che dovrebbe venire prima in realtà), una cosa importante da sapere è gli obiettivi principali di questo sistema (Perché il vostro cliente ha questo sistema per?). Conoscere gli obiettivi e tenerli a mente ti eviterà di separarti da questi obiettivi mantenendo il codice.

0

Costruire ed eseguire sarebbe la mia prima cosa. Inizia ad avere un'idea del problema che stava cercando di risolvere.

Forse è possibile importarlo in uno strumento UML come JUDE e ottenere un'immagine di come le classi interagiscono.

Scrivere i test JUnit è un eccellente suggerimento. Vorrei vedere quanto fosse stratificata l'app. Se è difficile da testare, forse è troppo accoppiato. Le unit test lo diranno. Ti daranno anche una rete di sicurezza se decidi che è necessario qualche refactoring.

JDK 1.4? È finita la fine della sua vita di supporto. Vorrei anche vedere se il codice dovrebbe essere compilato ed eseguito con JDK 5 come minimo, preferibilmente JDK 6. Forse potresti eseguire uno schiaffo di alcuni di questi test JUnit in JMeter e fare un test di caricamento di un povero uomo con 5 utenti simultanei.

Se si dispone di un modello di dati, inserirlo in ERWin e iniziare a vedere come le tabelle, gli oggetti e gli schermi fluiscono insieme.

2

Una cosa che mi aiuta a lavorare con il codice che è nuovo per me - questo è molto meno necessario per codice ben scritto - è di rifattenerlo in modo massivo per un giorno o due e poi di scartare tutte le mie modifiche. Questo processo mi aiuta a capire cosa fa il codice; lavorare con il codice mi aiuta a capirlo. Comincia anche a insegnarmi quali parti del codice sono fragili.

Se si ha la possibilità di migrare a una versione più recente di Java, la generizzazione di tutte le raccolte aiuterà a capire quali tipi di dati vengono passati.

Naturalmente, lo faccio dopo aver installato il software in un laboratorio di prova e aver giocato un po 'per capire cosa fa.

Modifica: Pensare alla mia risposta, inoltre, è utile per abilitare tutte le analisi e la registrazione diagnostica, per utilizzare il sistema e quindi per studiare i registri. Se la traccia del protocollo di comunicazione esiste, osservare questa traccia aiuterà a capire il protocollo di comunicazione utilizzato dal codice, magari con una traccia Wireshark dello stesso test.

Un'altra migrazione utile è la migrazione dalla vecchia libreria Concurrency alla nuova libreria di concessioni Java 5 (e 6). Questo ti aiuterà a capire dove sono i thread e quando vengono avviati e quando verranno chiusi.

Ovviamente, con eventuali modifiche al codice su un codebase sconosciuto, presumo che venga eseguito un test corretto per garantire che non vi sia nulla di rotto! Tuttavia, per bilanciare questo, ho imparato che dopo aver rifatto il codice scritto male, i bug introdotti sono spesso molto più facili da trovare rispetto agli errori che esistevano prima del refactoring.

0

Ovviamente si può provare a passare attraverso. È lento, ma una giornata passata a passare attraverso il codice potrebbe salvare una settimana di ricerca degli errori in seguito ...

Si potrebbe anche provare l'approccio di ricerca di scavenger, fare un elenco di ogni funzionalità e andare a caccia del codice per questo ...

0

Per prima cosa scremare il codice per comprenderne la struttura. Quindi, avvia l'app in modalità di debug e esegui l'operazione un paio di volte. Questo ti mostrerà il flusso e la struttura.

Utilizzare Netbeans per invertire i diagrammi delle classi.

1

C'è stato un discorso a cui ho partecipato Mike Hill in cui ha parlato di un processo che usa per trattare con un codice legacy errato. Lo ha chiamato "microtest" e praticamente sta isolando ogni cosa che vuoi testare (nella sua funzione o piccolo oggetto), e scrivendo dei test attorno ad esso. Questi test non significano affermare le cose in senso lato: servono solo a catturare lo stato in cui il sistema viene lasciato dopo che le linee sotto test sono state eseguite. In genere, affermava che le variabili avevano i valori che avevano nel debugger. Naturalmente, questi test sarebbero passati inizialmente, ma dopo averne scritti abbastanza ne avrebbe effettivamente avuto un'istantanea del sistema.

Una volta che questi test erano in atto, poteva iniziare a rifattorizzare quel pezzetto di codice per cercare di dare un senso a ciò che stava cercando di fare. Potrebbe farlo in modo sicuro perché i suoi "microtest" fallirebbero se cambiasse il modo in cui la funzione (o oggetto) si comportava anche leggermente.Mentre ciò significava che non poteva fare grandi cambiamenti di design, poteva eliminare un sacco di rumore e poteva ottenere una foto su cosa stava facendo il sistema. Una volta che aveva un'idea chiara di cosa stava ottenendo il codice, poteva iniziare a migliorarlo, trovare bug, ecc.

Non ci sono molte risorse gratuite su questo, motivo per cui non sto fornendo collegamenti. Spero di essere riuscito a descrivere abbastanza per darti qualche idea.

1

L'esperienza mi ha dimostrato che ci sono 3 obiettivi principali che si hanno quando si apprende un sistema legacy.

  • saperne di ciò che si suppone che il codice per fare
  • imparare come si fa loro
  • (cruciale) Scopri perché lo fa loro il modo in cui funziona

Tutti e tre di quelle parti sono molto importante, e ci sono alcuni trucchi per aiutarti a iniziare.

In primo luogo, resistere alla tentazione di fare semplicemente clic sul tasto Ctrl (o qualsiasi cosa l'IDE usi) per aggirare il codice per capire tutto. Probabilmente non sarai in grado di mantenere tutto in prospettiva nella tua mente in questo modo, specialmente quando ogni linea ti costringe a guardare più altre classi per capire di cosa si tratta.

Leggere la documentazione, ove possibile; di solito ti aiuta a ottenere rapidamente un quadro mentale su cui costruire tutto ciò che segue.

Esegui casi di test dove possibile.

Non aver paura di chiedere a qualcuno che sa se hai una domanda. Certo, non dovresti sprecare il tempo degli altri impiegati con domande inutili, ma se c'è qualcosa che semplicemente non capisci (questo è particolarmente vero con domande più concettuali come "Non avrebbe molto più senso implementarlo come a ___ "o qualcosa del genere), probabilmente vale la pena di trovare la risposta prima di incasinare qualcosa e non so perché.

Quando finalmente riesci a leggere il codice, inizia da un punto logico "principale" e vai da lì. Non basta leggere il codice dall'alto verso il basso, o in ordine alfabetico o altro (questo è probabilmente ovvio).