Il modo migliore per caricare le impostazioni dell'applicazione

22

Un modo semplice per mantenere le impostazioni di un'applicazione Java è rappresentato da un file di testo con estensione ".properties" contenente l'identificativo di ogni impostazione associata a un valore specifico (questo valore può essere un numero, una stringa, una data, ecc. ..). C # usa un approccio simile, ma il file di testo deve essere chiamato "App.config". In entrambi i casi, nel codice sorgente è necessario inizializzare una classe specifica per le impostazioni di lettura: questa classe ha un metodo che restituisce il valore (come stringa) associato all'identificatore di impostazione specificato.

// Java example
Properties config = new Properties();
config.load(...);
String valueStr = config.getProperty("listening-port");
// ...

// C# example
NameValueCollection setting = ConfigurationManager.AppSettings;
string valueStr = setting["listening-port"];
// ...

In entrambi i casi dovremmo analizzare le stringhe caricate dal file di configurazione e assegnare i valori convertiti agli oggetti tipizzati relativi (gli errori di parsing potrebbero verificarsi durante questa fase). Dopo la fase di analisi, dobbiamo verificare che i valori di impostazione appartengano a uno specifico dominio di validità: ad esempio, la dimensione massima di una coda dovrebbe essere un valore positivo, alcuni valori potrebbero essere correlati (esempio: min < max), e così via.

Supponiamo che l'applicazione carichi le impostazioni non appena viene avviata: in altre parole, la prima operazione eseguita dall'applicazione è caricare le impostazioni. Tutti i valori non validi per le impostazioni devono essere sostituiti automaticamente con i valori predefiniti: se questo accade a un gruppo di impostazioni correlate, tali impostazioni sono tutte impostate con valori predefiniti.

Il modo più semplice per eseguire queste operazioni è creare un metodo che analizzi prima tutte le impostazioni, quindi controlla i valori caricati e infine imposta qualsiasi valore predefinito. Tuttavia, la manutenzione è difficile se si utilizza questo approccio: poiché il numero di impostazioni aumenta durante lo sviluppo dell'applicazione, diventa sempre più difficile aggiornare il codice.

Per risolvere questo problema, avevo pensato di utilizzare il modello Metodo modello , come segue.

public abstract class Setting
{
    protected abstract bool TryParseValues();

    protected abstract bool CheckValues();

    public abstract void SetDefaultValues();

    /// <summary>
    /// Template Method
    /// </summary>
    public bool TrySetValuesOrDefault()
    {
        if (!TryParseValues() || !CheckValues())
        {
            // parsing error or domain error
            SetDefaultValues();
            return false;
        }
        return true;
    }
}

public class RangeSetting : Setting
{
    private string minStr, maxStr;
    private byte min, max;

    public RangeSetting(string minStr, maxStr)
    {
        this.minStr = minStr;
        this.maxStr = maxStr;
    }

    protected override bool TryParseValues()
    {
        return (byte.TryParse(minStr, out min)
            && byte.TryParse(maxStr, out max));
    }

    protected override bool CheckValues()
    {
        return (0 < min && min < max);
    }

    public override void SetDefaultValues()
    {
        min = 5;
        max = 10;
    }
}

Il problema è che in questo modo abbiamo bisogno di creare una nuova classe per ogni impostazione, anche per un singolo valore. Esistono altre soluzioni a questo tipo di problema?

In breve:

  1. Manutenzione semplificata: ad esempio, l'aggiunta di uno o più parametri.
  2. Estensibilità: una prima versione dell'applicazione può leggere un singolo file di configurazione, ma le versioni successive possono dare la possibilità di una configurazione multiutente (l'amministratore imposta una configurazione di base, gli utenti possono impostare solo determinate impostazioni, ecc.) .
  3. Progettazione orientata agli oggetti.
posta enzom83 16.08.2012 - 01:33
fonte

5 risposte

7

Dropwizard utilizza un approccio YAML

Essenzialmente il file di configurazione esterno è codificato come documento YAML. Questo viene poi analizzato durante l'avvio dell'applicazione e mappato a un oggetto di configurazione.

Il risultato finale è robusto e soprattutto semplice da gestire.

    
risposta data 16.08.2012 - 18:39
fonte
6

Consideriamo questo da due punti di vista: l'API per ottenere i valori di configurazione e il formato di archiviazione. Sono spesso correlati, ma è utile considerarli separatamente.

API di configurazione

Il modello del metodo Template è molto generico, ma mi chiedo se hai davvero bisogno di quella generalità. Avresti bisogno di una classe per ogni tipo di valore di configurazione. Ne hai davvero tanti tipi? Immagino che potresti cavartela con solo una manciata: archi, intarsi, galleggianti, booleani ed enumerati. Detto questo, potresti avere una classe Config che ha una manciata di metodi su di essa:

int getInt(name, default, min, max)
float getFloat(name, default, min, max)
boolean getBoolean(name, default)
String getString(name, default)
<T extends Enum<T>> T getEnum(name, Class<T> enumClass, T default)

(Penso di aver avuto i generici su quest'ultimo a destra.)

Fondamentalmente ciascun metodo sa come gestire l'analisi del valore della stringa dal file di configurazione e per gestire gli errori e, se necessario, restituire il valore predefinito. Il controllo di intervallo per i valori numerici è probabilmente sufficiente. Potresti desiderare sovraccarichi che omettono i valori dell'intervallo, il che equivale a fornire un intervallo di Integer.MIN_VALUE, Integer.MAX_VALUE. Un Enum è un modo sicuro per la convalida di una stringa contro un set fisso di stringhe.

Ci sono alcune cose che questo non gestisce, come più valori, valori che sono correlati, ricerche di tabelle dinamiche, ecc. Potresti scrivere routine di analisi e convalida specializzate per queste, ma se questo diventa troppo complicato, vorrei inizia a chiedersi se stai provando a fare troppo con un file di configurazione.

Formato di archiviazione

I file di proprietà Java sembrano adatti per la memorizzazione di coppie di valori-chiave individuali e supportano abbastanza bene i tipi di valori sopra descritti. Potresti anche prendere in considerazione altri formati come XML o JSON, ma probabilmente questi sono overkill a meno che tu non abbia dati annidati o ripetuti. A quel punto sembra molto al di là di un file di configurazione ....

Telastyn menzionava oggetti serializzati. Questa è una possibilità, sebbene la serializzazione abbia le sue difficoltà. È binario, non testo, quindi è difficile vedere e modificare i valori. Devi occuparti della compatibilità di serializzazione. Se mancano dei valori dall'input serializzato (ad esempio, hai aggiunto un campo alla classe Config e stai leggendo un vecchio formato serializzato di esso), i nuovi campi vengono inizializzati su null / zero. È necessario scrivere la logica per determinare se inserire un altro valore predefinito. Ma uno zero indica l'assenza di un valore di configurazione o è stato specificato come zero? Ora devi eseguire il debug di questa logica. Infine (non è sicuro se questo è un problema), potrebbe essere ancora necessario convalidare i valori nel flusso di oggetti serializzato. È possibile (anche se inopportuno) che un utente malintenzionato modifichi un flusso di oggetti serializzato in modo non rilevabile.

Direi di restare con le proprietà se possibile.

    
risposta data 16.08.2012 - 09:25
fonte
3

Come l'ho fatto:

Inizializza tutto su valori predefiniti.

Analizza il file, memorizzando i valori man mano che procedi. I luoghi impostati sono responsabili di garantire che i valori siano accettabili, i valori errati vengono ignorati (e quindi conservano il valore predefinito).

    
risposta data 16.08.2012 - 02:16
fonte
2

Are there other solutions to this kind of problem?

Se tutto ciò di cui hai bisogno è una semplice configurazione, mi piace fare una semplice vecchia lezione per questo. Inizializza i valori predefiniti e può essere caricato dal file dall'app tramite le classi di serializzazione incorporate. L'app passa poi alle cose che ne hanno bisogno. Non preoccuparti di parsing o conversioni, niente strappi con le stringhe di configurazione, niente spreco di casting. E rende la via di configurazione più facile da usare per scenari in-codice dove deve essere salvata / caricata dal server o come preset e modo più facile da usare nella tua unità test.

    
risposta data 16.08.2012 - 01:41
fonte
2

Almeno in .NET, puoi facilmente creare i tuoi propri oggetti di configurazione strongmente tipizzati - vedi questo Articolo MSDN per un rapido esempio.

Protip: avvolgi la tua classe di configurazione in un'interfaccia e lascia parlare la tua applicazione. Semplifica l'inserimento di configurazioni false per test o per profitto.

    
risposta data 16.08.2012 - 04:25
fonte

Leggi altre domande sui tag