2011-04-11 19 views
19

Recentemente sono stato sempre più frustrato da un problema che vedo emergere nel mio codice di progetto.Tenere traccia delle classi di utilità

Sto lavorando a un progetto java su larga scala che ha> 1M di codice. Le interfacce e la struttura delle classi sono progettate molto bene e gli ingegneri che scrivono il codice sono molto abili. Il problema è che, nel tentativo di rendere il codice più pulito, le persone scrivono classi di utilità ogni volta che devono riutilizzare alcune funzionalità, di conseguenza nel tempo e man mano che il progetto aumenta, crescono sempre più metodi di utilità. Tuttavia, quando il prossimo ingegnere incontra la necessità della stessa funzionalità, non ha modo di sapere che qualcuno ha già implementato una classe di utilità (o un metodo) da qualche parte nel codice e implementa un'altra copia della funzionalità in una classe diversa. Il risultato è un sacco di duplicazione del codice e troppe classi di utilità con funzionalità sovrapposte.

Esistono strumenti o principi di progettazione che noi, in quanto team, possiamo implementare al fine di prevenire la duplicazione e la scarsa visibilità delle classi di utilità?

Esempio: ingegnere A ha 3 posti di cui ha bisogno per trasformare XML a String così scrive una classe di utilità chiamato XMLUtil e pone un metodo statico toString(Document) in esso. L'ingegnere B ha diversi luoghi in cui serializza i documenti in vari formati, tra cui String, quindi scrive una classe di utilità chiamata SerializationUtil e ha un metodo statico chiamato serialize(Document) che restituisce una stringa.

Si noti che questo è più di una semplice duplicazione di codice in quanto è del tutto possibile che le 2 implementazioni dell'esempio precedente siano diverse (ad esempio una utilizza l'API del trasformatore e l'altra utilizza Xerces2-J), quindi può essere vista come una "best-practice" problema pure ...

Aggiornamento:. immagino che meglio descrivere l'attuale contesto sviluppiamo in usiamo Hudson per cI, trifoglio per la copertura del codice e Checkstyle per analisi statica del codice. Utilizziamo uno sviluppo agile che include colloqui giornalieri e (forse insufficienti) recensioni di codici. Definiamo tutte le nostre classi di utilità in un file .util che a causa delle sue dimensioni ha ora 13 sotto-pacchetti e circa 60 classi sotto la classe radice (.util). Usiamo anche le librerie di terze parti come la maggior parte dei barattoli di Apache Commons e alcuni dei barattoli che costituiscono Guava.

sono sicuro che siamo in grado di ridurre la quantità di utilità della metà se mettiamo qualcuno il compito di refactoring che dell'intero pacchetto, mi chiedevo se ci sono strumenti che possono rendere tale operazione meno costosa, e se non ci sono tutte le metodologie che possono ritardare il più possibile il problema da ricorrere.

+0

Vedere http://stackoverflow.com/questions/4188919/how-to-search-for-java-api-methods-by-type-signature – meriton

+0

Immagino che sia possibile adattare questa soluzione per elencare tutti i metodi con duplicati digitare le firme. – meriton

risposta

5

Il tuo problema è molto comune. E anche un vero problema, perché non c'è una buona soluzione.

Siamo nella stessa situazione qui, beh direi peggio, con 13 milioni riga di codice, fatturato e più di 800 sviluppatori che lavorano sul codice. Discutiamo spesso dello stesso problema che descrivi.

La prima idea - che gli sviluppatori hanno già utilizzato - è quello di refactoring del codice comune in alcune classi di utilità. Il nostro problema con questa soluzione, anche con la programmazione, il mentoring e la discussione di coppia, è che siamo semplicemente troppi perché questo sia efficace. In effetti, cresciamo in sottogruppi, con persone che condividono la conoscenza nel loro sottogruppo, ma la conoscenza non transita tra i sottogruppi. Forse abbiamo torto, ma penso che anche la programmazione e le conversazioni di coppia non possano essere d'aiuto in questo caso.

Abbiamo anche un team di architettura. Questo team è responsabile di occuparsi dei problemi di progettazione e architettura e di creare utilità comuni di cui potremmo aver bisogno. Questo team produce infatti qualcosa che potremmo definire un framework aziendale. Sì, è un framework, ea volte funziona bene. Questo team è anche responsabile di promuovere le migliori pratiche e aumentare la consapevolezza su cosa dovrebbe essere fatto o meno, cosa è disponibile o cosa non lo è.

buon Core Design Java API è uno dei motivi per il successo di Java. Anche le buone librerie di open source di terze parti contano molto. Anche una piccola API ben realizzata consente di offrire un'astrazione davvero utile e può aiutare a ridurre le dimensioni del codice molto. Ma sai, creare framework e API pubbliche non è la stessa cosa, basta codificare una classe di utilità in 2 ore. Ha un costo davvero alto. Una classe di utilità costa 2 ore per la codifica iniziale, forse 2 giorni con i test di debug e unità. Quando inizi a condividere codice comune su grandi progetti/team, crei davvero un'API. È necessario garantire una documentazione perfetta quindi, codice veramente leggibile e gestibile. Quando rilasci una nuova versione di questo codice, devi rimanere compatibile con le versioni precedenti. Devi promuoverlo a livello aziendale (o almeno a livello di squadra). A partire da 2 giorni per la tua piccola classe di utilità, raggiungi 10 giorni, 20 giorni o addirittura 50 giorni per un'API completa.

ed il vostro disegno API non può essere così grande. Beh, non è che i tuoi ingegneri non siano brillanti, anzi lo sono. Ma sei disposto a lasciarli lavorare per 50 giorni su una piccola classe di utilità che aiuta semplicemente ad analizzare il numero in modo coerente per l'interfaccia utente? Sei disposto a lasciare che ridisegnino il tutto quando inizi a utilizzare un'interfaccia utente mobile con esigenze completamente diverse? Inoltre hai notato come gli ingegneri più brillanti del mondo rendono le API che non saranno mai popolari o si sbiadiranno lentamente? Vedete, il primo progetto web che abbiamo realizzato utilizzava solo framework interni o nessun framework. Abbiamo quindi aggiunto PHP/JSP/ASP. Poi in Java abbiamo aggiunto Struts. Ora JSF è lo standard. E stiamo pensando di usare Spring Web Flow, Vaadin o Lift ...

Tutto quello che voglio dire è che non esiste una buona soluzione, il sovraccarico cresce in modo esponenziale con la dimensione del codice e la dimensione della squadra. La condivisione di una grande base di codici limita la tua agilità e reattività.Qualsiasi cambiamento deve essere fatto con attenzione, è necessario pensare a tutti i potenziali problemi di integrazione e tutti devono essere formati sulle nuove specificità e caratteristiche.

Ma il punto di produttività principale in una società di software non è di ottenere 10 o anche 50 righe di codice durante l'analisi di XML. Un codice generico per fare questo crescerà di un migliaio di righe di codice e ricrea una complessa API che sarà stratificata per classi di utilità. Quando il ragazzo crea una classe di utilità per l'analisi di XML, è una buona astrazione. Dà un nome a una dozzina o addirittura a cento linee di codice specializzato. Questo codice è utile perché è specializzato. L'API comune consente di lavorare su stream, URL, stringhe, qualunque cosa. Ha una fabbrica in modo da poter scegliere l'implementazione del parser. La classe di utilità è buona perché funziona solo con questo parser e con le stringhe. E perché hai bisogno di una riga di codice per chiamarla. Ma ovviamente, questo codice di utilità è di uso limitato. Funziona bene per questa applicazione mobile o per caricare la configurazione XML. Ed è per questo che lo sviluppatore ha aggiunto la classe di utilità per questo in primo luogo.

In conclusione, quello che io considero invece di cercare di consolidare il codice per l'intera base di codice è quello di dividere il codice di responsabilità come le squadre crescono:

  • trasformare il vostro grande squadra che funziona su un unico grande progetto in piccolo team che lavorano su diversi sottoprogetti;
  • assicurare che l'interfaccia sia buona per minimizzare i problemi di integrazione, ma lasciare che il team abbia il proprio codice;
  • all'interno di questi gruppi e codebase corrispondenti, assicurarsi di avere le migliori pratiche. Nessun codice duplicato, buone astrazioni. Utilizza le API comprovate esistenti dalla community. Utilizza la programmazione di coppie, una solida documentazione API, wiki ... Ma dovresti davvero consentire a team diversi di fare le loro scelte, creare il proprio codice, anche se questo significa duplicare il codice tra team o decisioni di progettazione differenti. Sai, se le decisioni di progettazione sono diverse, ciò potrebbe essere dovuto al fatto che le esigenze sono diverse.

Quello che stai veramente gestendo è la complessità. Alla fine, se crei una base di codice monolitica, molto generica e avanzata, aumenti il ​​tempo necessario per i nuovi arrivati, aumenti il ​​rischio che gli sviluppatori non utilizzino affatto il tuo codice comune e rallenti tutti perché qualsiasi cambiamento ha molte più possibilità di rompere le funzionalità esistenti.

+0

grazie per la tua risposta, penso che tu abbia ragione. Il prossimo passo logico per noi è la suddivisione del nostro codice in sottoprogetti e la definizione di interfacce in cui il codice condiviso è minimo. Ne abbiamo parlato per un po 'e sento che fornirà il compromesso che cerchiamo. Dato che non sono sicuro che esista uno strumento che possa aiutarmi a risolvere questo problema, ti assegnerò la generosità, poiché probabilmente sei il più vicino a definire un modo per mitigare il problema. – Asaf

+0

50 giorni suddivisi su 800 potenziali utenti non è tanto un overhead –

+0

Nessuno vuole pagare 50 giorni per una cosa che potrebbe essere fatta in 2 giorni. E se devi farlo perché hai una grande squadra, allora questa grande squadra sta di fatto diminuendo la produttività. –

4

Ci sono diverse pratiche XP/agili è possibile utilizzare per risolvere questo problema, ad esempio:

  • colloquio con l'altro (ad esempio, durante la riunione quotidiana stand-up)
  • paio recensione programmazione/code

Quindi creare, documentare & test uno o più progetti di librerie di utilità a cui è possibile fare riferimento. Raccomando di usare Maven per gestire dipendenze/versioni.

+0

ci stiamo lentamente muovendo verso questa soluzione esternalizzando sempre più utilità in diversi vasi (un po 'come i comuni apache).Tuttavia, c'è ancora il problema di ordinare le utenze all'interno di quei vasi (ciò che appartiene dove e dove dovrei guardare). So che la comunicazione è imperativa ma diverse persone lavorano su diverse parti del codice e senza alcun modo per un ingegnere di accedere al dati è destinato a perdere qualcosa – Asaf

+0

Forse sbaglio, ma un codice base con 1 milione di righe di codice è troppo grande, quindi non puoi sperare di gestire il codice duplicato solo con la programmazione di conversazione e coppia. –

3

Si potrebbe pensare di suggerire che tutte le classi di utilità siano collocate in una struttura di pacchetto ben organizzata come com.yourcompany.util.. Se le persone sono disposte a dare un nome ai pacchetti secondari e alle classi, allora almeno se hanno bisogno di trovare un'utilità, sanno dove cercare.Non penso che ci sia una risposta di pallottola d'argento qui però. La comunicazione è importante Forse se uno sviluppatore invia una semplice e-mail al resto dello staff di sviluppo quando scrive una nuova utility, sarà sufficiente per farlo sul radar della gente. O una pagina wiki condivisa in cui le persone possono elencarle/documentarle.

+0

usiamo la struttura del pacchetto .util, tuttavia con il crescere del numero di utility diventa un problema trovare lo strumento giusto per il lavoro che si conclude con altre classi. un Wiki su tutti gli utils disponibili potrebbe essere un'opzione, ma poi trovare le informazioni nel wiki è (approssimativamente) lo stesso problema di trovare le informazioni nel Javadoc. – Asaf

1
  1. squadra comunicazione (gridare "hey fa qualcuno ha un toString documento?")
  2. Tenere classi di utilità ad un minimo assoluto e li limita ad un unico namespace
  3. pensano sempre: come posso fare questo con un oggetto. Nel tuo esempio, estenderei la classe Document e aggiungerò i metodi toString e serialize.
+0

Credo che stiamo facendo un buon sforzo per mantenere le funzionalità comuni nelle classi base, tuttavia, spesso l'estensione delle volte non è un'opzione, "org.w3c.Document" è un ottimo esempio. Forse potremmo fare di più spostando più logica in classi base, ma come troverei tutti i metodi di utilità esistenti che possono essere spostati nelle classi base? – Asaf

+0

@well, un modo sarebbe quello di fare il grep in giro per "public static" o alcuni di questi pattern nelle rispettive cartelle. E sì, ti sento nelle classi base "sigillate". In questi casi puoi sempre usare il motivo decorativo per avvolgere Documento nella tua versione personale. – Milimetric

0

Questo problema è utile quando si combinano le funzionalità IDE "code-completion" con le lingue che supportano le estensioni di tipo (ad esempio C# e F #). In modo che, immaginando Java aveva un una caratteristica del genere, un programmatore potrebbe esplorare tutti i metodi di estensione in una classe con facilità all'interno del IDE come:

Document doc = ... 
doc.to //list pops up with toXmlString, toJsonString, all the "to" series extension methods 

Naturalmente, Java non ha le estensioni di tipo. Ma potresti usare grep per cercare il tuo progetto per "tutti i metodi pubblici statici che considerano SomeClass come il primo argomento" per ottenere informazioni simili su quali metodi di utilità sono già stati scritti per una determinata classe.

+0

conosci qualche strumento o plugin di eclipse che può farlo facilmente? chiedere a tutti i programmatori di utilizzare il loro pacchetto di utilità prima di aggiungere qualsiasi metodo statico sembra un problema e temo che la richiesta venga molto probabilmente ignorata per la sua natura eccessivamente complessa. – Asaf

+1

Mi dispiace @Asaf, non so di uno strumento così pronto. Ma penso che questo potrebbe essere un piccolo progetto divertente. Non uso molto grep, ma immagino che il pattern sia ragionevolmente semplice e facilmente riutilizzabile per qualsiasi classe. Una volta capito, è possibile creare uno script che prende il nome di una classe come unico input ed esegue il comando. Questo porterebbe a termine il lavoro con poco onere su tutti i programmatori, penso. Ma sì, sarebbe fantastico avere un plugin Eclipse in cui si fa clic con il pulsante destro del mouse sul nome di una classe e si sceglie ("trova tutti i metodi di utilità") ... che potrebbe essere anche un progetto divertente! –

+0

inviando il tuo commento per avermi dato un'idea per il mio prossimo sprint "gratuito" – Asaf

0

È piuttosto difficile creare uno strumento che riconosca la "stessa funzionalità". (In teoria questo è di fatto impossibile, e dove puoi farlo in pratica probabilmente hai bisogno di un dimostratore di teoremi).

Ma quello che succede spesso è clonare le persone che è vicino a ciò che vogliono, e quindi personalizzarlo. Quel tipo di codice che è possibile trovare, utilizzando un rilevatore di cloni.

Il nostro CloneDR è uno strumento per il rilevamento del codice clonato esatto e quasi-miss basato sull'utilizzo di alberi di sintassi parametrizzati. Corrisponde a versioni analizzate del codice, quindi non viene confuso dal layout, dai commenti modificati, dai nomi delle variabili rivisti o, in molti casi, da istruzioni inserite o cancellate. Esistono versioni per molti linguaggi (C++, COBOL, C#, Java, JavaScript, PHP, ...) e puoi vedere esempi di esecuzioni di rilevamento clone sul link fornito . Normalmente trova il codice duplicato del 10-20% e se si estrae quel codice in metodi di libreria su base religiosa, la base di codice può effettivamente ridursi (ciò si è verificato con un'organizzazione che utilizza CloneDR).

0

Siete alla ricerca di una soluzione che si può aiutare a gestire questo problema inevitabile, quindi posso suggerire uno strumento:

  • TeamCity: un sorprendente prodotto facile da usare che gestisce tutto il vostro codice costruzione automatizzata dal repository ed esegue test unitari ecc.
    È persino un prodotto gratuito per la maggior parte delle persone.
    La parte ancora migliore: ha incorporato code duplicate detection across all your code.

più roba da leggere:

0
  1. un progetto di utilità applicazione standard.creare un jar con l'ambito dell'estensione e il pacchetto ristretti in base alle funzionalità.
  2. utilizzare le utilità comuni come apache-commons o collezioni Google e fornire un'astrazione
  3. mantengono base di conoscenze e di documentazione e JIRA di monitoraggio per i bug e miglioramenti
  4. refactoring evolutivo
  5. findbugs e PMD per la ricerca di duplicazione del codice o bug
  6. rivedere e testare strumenti di utilità per le prestazioni
  7. util karma! chiedere ai membri del team di contribuire alla base del codice, ogni volta che ne trovano uno nel codice giungla esistente o ne richiedono di nuovi.
9

Una buona soluzione a questo problema è iniziare ad aggiungere più orientamento all'oggetto. Per usare il tuo esempio:

Esempio: ingegnere A ha 3 posti di cui ha bisogno per trasformare XML a String così scrive una classe di utilità chiamato XMLUtil e pone un metodo toString statico (Documento) in esso

La soluzione è smettere di usare tipi primitivi o tipi forniti da JVM (String, Integer, java.util.Date, java.w3c.Document) e avvolgerli nelle proprie classi specifiche del progetto. Quindi la classe XmlDocument può fornire un metodo toString conveniente e altri metodi di utilità. Il tuo ProjectFooDate può contenere i metodi di parsing e di formattazione che altrimenti finirebbero in varie classi DateUtils, ecc.

In questo modo, l'IDE ti chiederà i tuoi metodi di utilità ogni volta che proverai a fare qualcosa con un oggetto.

+0

Sì, decisamente verso un modello basato su domini/architettura esagonale –

Problemi correlati