Gestione dei percorsi delle risorse nel codice sorgente

4

Sospetto che sia qualcosa che molti di voi hanno affrontato, e sono certo che ci sono articoli su come farlo, ma non riesco a trovarli.

Il mio problema è che le mie varie classi costanti in Java (leggi: le classi contenenti liste di valori costanti statici) sono troppe e troppo grandi, e alcune di esse hanno alcuni fastidiosi inizializzatori statici complessi.

Abbiamo una classe di percorsi di file, una classe di nomi di file, una classe di costanti, una classe di stringhe, una classe di colori (per i componenti di swing) e un mucchio di css per i componenti di JavaFX ... continua sopra. Il CSS è gestibile, il resto sta diventando rapidamente non così.

Avere classi come questa:

public class Constants {
    public static final String something = "Something";
    public static final Path someResourcePath = staticMethodCall(RootString, dirTraversal);
    public static final Image spinner = iconFindingStaticMethod(dirTraversal)
    //and on and on for hundreds (not yet thousands thankfully) of lines

    //and then often we have
    static{
        //code that determines our environment and fudges some paths as appropriate
        //sometimes we ask the class loader to find some things for us
        //nothing too scary, but still, static initializers are never friendly.
    }
}

va bene per una ragione molto potente: ogni programmatore java là fuori sa esattamente cosa fa questa classe e come funziona. Quelli con inizializzatori statici potrebbero essere un po 'confusi, ma per la maggior parte, quanto sopra è PO-Static-JO. Mi piace.

Ma introduce anche una serie di problemi. Proverò a elencare quelli che conosco qui:

  1. l'odore di codice più ovvio di tutti: queste classi si stanno moltiplicando e sono tutte fastidiosamente grandi. Ne abbiamo uno per le estensioni di file che contiene una dozzina di voci, incluse cose come public static final String XMLFileExtension = "xml" . Potrebbe essere un po 'troppo, ma non sono davvero disposto a criticare lo sviluppatore che l'ha scritto. Non sarebbe difficile sostituire i riferimenti con il valore stringa, ma mi piacerebbe incoraggiare questo tipo di codice, quindi mi sentirei stupido a farlo. Ma queste classi stanno diventando grandi e fastidiose.
  2. l'uso di costanti statiche non è testabile. Questo di per sé non è troppo spesso un problema poiché i valori in quelle classi dovrebbero essere semplici, ma si trasformano in un problema quando fai qualcosa come Constants.someFilePath.exists() ? a() : b() (poiché non c'è modo di di intercettare quel exists() call , da qualsiasi ambiente chiederemo al file system attuale se quel percorso esiste, che è un problema per i test.) Siamo in vizio, quindi mi piacerebbe molto iniettare questi valori come tutto il resto, quindi possono essere modificati dai nostri dispositivi con valori falso / stub / mock.
  3. Introduce anche un problema di dipendenza. In questo momento abbiamo un paio di IntelliJ & Guice Modules (basi di codici semi-separati) sotto lo stesso repository git. Tutti usano i file costanti del nostro modulo originale, il che significa che tutti i moduli hanno una dipendenza con quel modulo, ma con costanti statiche qual è la soluzione? Copia incolla?

Alcune soluzioni che ho pensato:

  • potremmo sfruttare il guice e semplicemente iniettare ogni singola costante necessaria con un parametro @Named . Questo risolve i problemi 2 e amp; 3, ma comporta il problema 1 poiché la logica di legame dovrebbe essere incredibilmente lunga e contenere una quantità enorme di registrazione duplicata, o impiegare una composizione piuttosto complessa.
  • potremmo utilizzare la rotta auto-generate-source-java-da-XML-o-JSON, che ho utilizzato con Android e credo che alcuni componenti C # su cui ho lavorato brevemente. Il passaggio extra nel processo di costruzione aggiunge un livello di complessità in un'area (ovvero: devops) che non è familiare a molti sviluppatori. È stato bello avere solo una grande tabella in Eclipse che contenga tutti i miei valori, ma la complessità aggiunta al processo di generazione è indesiderata. Inoltre, la logica di inizializzazione statica potrebbe non adattarsi a questo modello.
  • mantieni il corso con Java, magari estrai un pacchetto vicino alla rotta chiamata "Ambiente" o "costanti" e poi dai a quel pacchetto un numero di classi costanti che possono ereditare altre classi con campi condivisi. Per quanto riguarda la testabilità, eseguiamo java inlining dei campi finali. Potremmo sostituire ogni campo con un semplice getter, ma questo rende un problema già verbosamente più verboso.

Non so davvero come voglio affrontarlo. Mi sono detto di lasciarlo diventare un problema per alcuni mesi, pensando che sarebbe emersa una sorta di soluzione. Non lo è, e sappiamo che abbiamo un grafico di dipendenza grossa, hacks attorno al test del codice con percorsi di file e grandi classi costanti.

Che tipo di soluzioni hai impiegato per gestire questi file? Sono eccessivamente preoccupato e quello che abbiamo ora è la soluzione migliore?

    
posta Groostav 16.05.2014 - 22:00
fonte

1 risposta

2

Se si dispone di inizializzatori statici di grandi dimensioni, probabilmente non si definiscono tanto le costanti quanto i valori calcolati nella cache che verranno mantenuti per tutta la durata della propria applicazione.

Potresti avere una migliore sorte di astrazione sulla funzionalità piuttosto che sui valori. Verifica se riesci a identificare i modelli in cui utilizzi queste numerose costanti, estrai questi metodi in metodi e inserisci questi metodi in interfacce. Accedi alle tue costanti anche attraverso i metodi.

Esempio Java:

interface Platform {
    Image getSpinnerImage();
    // ...
}

// could be a caching proxy
class LocalPlatform implements Platform {
    private Image spinnerImage;
    public Image getSpinnerImage() {
        if ( spinnerImage == null ) {
            spinnerImage = loadSpinnerImage();
        }
        return spinnerImage;
    }
}

Questo è sia testabile come iniettabile, che dovrebbe occuparsi di inizializzatori statici di grandi dimensioni.

Questo non vuol dire che dovresti eliminare tutte le costanti; mantenere costanti che sono proprio questo. Avere getXmlFileExtension() è pazzesco perché qualsiasi altra implementazione di return "xml" viola il principio di sorpresa minima, ma qualcosa come getDefaultInstallationPath() è un candidato migliore.

Considerate sufficienti queste interfacce, passerete a ciò che avete già iniziato a sperimentare: una sorta di progetto di piattaforma dal quale dipenderanno molte delle vostre altre parti. Questo va bene; proprio come Java ha java.lang , avrai il tuo ambiente / piattaforma.

Ho usato questa tecnica per raggiungere un certo successo in progetti di grandi dimensioni che presentavano problemi simili a quelli che hai descritto. Non ha drasticamente ridotto le linee di codice, ma ha incapsulato quel codice, facilitato il debug e la verifica e ha finito per semplificare gran parte del resto del progetto.

Nota: se ritieni che alcune parti del tuo codice siano problematiche, puoi sempre provare a presentare il tuo codice effettivo alle persone su Revisione del codice .

    
risposta data 17.05.2014 - 21:10
fonte

Leggi altre domande sui tag