2010-03-23 17 views
30

Sono spesso confrontato con il problema di memorizzare, in memoria, alcune (eventualmente complesse) impostazioni di configurazione caricate dai file sul filesystem. Mi chiedo se ci sia un modo migliore per progettare un modello per questo problema, rispetto a quello che ho usato.Design Pattern per proprietà di configurazione caricate una tantum?

In sostanza, la mia soluzione attuale prevede tre passaggi.

  1. Costruire un singleton. Poiché i dati sono persistenti e non si verificano cambiamenti durante il runtime dell'applicazione, dovrebbe essere necessaria solo un'istanza di un oggetto.

  2. Quando viene effettuata la prima richiesta per l'oggetto, creare l'oggetto e leggere da un file.

  3. Esporre i dati con getter.

Questo ha l'effetto che un sacco del mio codice è simile al seguente: MyConfiguration.getInstance().getWeightOfBomb(), che sembra piuttosto strano per me.

C'è un modo migliore per gestirlo in modo più semantico?

risposta

29

Iniezione di dipendenza. Non devi necessariamente utilizzare un framework DI come Spring o Guice ma vuoi davvero evitare di sporcare il tuo codice con singleton. È ancora possibile utilizzare un singleton nell'implementazione, ma non c'è motivo per cui il resto del codice debba sapere che si tratta di un singleton. I singleton sono un grande dolore quando si effettuano test e refactoring dell'unità. Lascia che il tuo codice faccia riferimento ad un'interfaccia. per esempio,

interface MyConfig { 
    double getWeightOfBomb(); 
} 

class SomeClass { 
    private MyConfig myConfig; 

    public void doSomething() { 
     myConfig.getWeightOfBomb(); 
    } 
} 

class MyConfigImpl implements MyConfig { 
    public double getWeightOfBomb() {   
      return MyConfiguration.getInstance().getWeightOfBomb(); 
    } 
} 

Se si utilizza un quadro DI, proprio setup che le classi di avere l'implementazione MyConfig iniettato. Se non lo fai, allora l'approccio più pigro che ha ancora tutti i benefici è quello di fare qualcosa di simile:

class SomeClass { 
    private MyConfig myConfig = new MyConfigImpl();   
} 

davvero tocca a voi. L'importante è che sia possibile sostituire myConfig in base all'istanza quando in seguito si accorge che è necessario variare il comportamento e/o il test dell'unità.

+3

Un sacco di volte, ho solo bisogno di racchiudere alcune proprietà di configurazione in piccole app di console (che ho scritto in massa, ultimamente) . Nelle applicazioni più grandi, sebbene (per il quale ho adottato questo modello), questo metodo funziona bene. –

2

Proposta aggiuntiva per la risposta di Noah.

Se è scomodo scrivere un metodo per ogni parametro di configurazione, è possibile utilizzare enum per questo. Ecco cosa intendo:

public class Configuration { 

private final Properties properties; 

public enum Parameter { 
    MY_PARAMETER1("my.parameter1", "value1"), 
    MY_PARAMETER2("my.parameter2", "value2"); 

    private final String name; 
    private final String defaultValue; 

    private Parameter(String name, String defaultValue) { 
     this.name = name; 
     this.defaultValue = defaultValue; 
    } 

    private String getName() { 
     return name; 
    } 

    private String getDefaultValue() { 
     return defaultValue; 
    } 
} 


public Configuration(Properties properties) { 
    this.properties = (Properties)properties.clone(); 
} 

//single method for every configuration parameter 
public String get(Parameter param) { 
    return properties.getProperty(param.getName(), param.getDefaultValue()); 
} 

}

Dopo di che, se si dispone di un nuovo parametro di configurazione, tutto quello che dovete fare è aggiungere una nuova voce enum.

È anche possibile estrarre un'interfaccia dalla classe di configurazione, ovviamente spostando enum all'esterno.

3

Si potrebbe creare un'interfaccia per rappresentare la configurazione:

public interface Config { 
    interface Key {} 
    String get(Key key); 
    String get(Key key, String defaultValue); 
} 

E un'implementazione Singleton:

public enum MyConfig implements Config { 
    INSTANCE("/config.properties"); 
    private final Properties config; 

    MyConfig(String path) { 
     config = new Properties(); 
     try { 
      config.load(this.getClass().getResourceAsStream(path)); 
     } catch (IOException | NullPointerException e) { 
      throw new ExceptionInInitializerError(e); 
     } 
    } 

    @Override 
    public String get(Config.Key key){ 
     return config.getProperty(key.toString()); 
    } 

    @Override 
    public String get(Config.Key key, String defaultValue) { 
     return config.getProperty(key.toString(), defaultValue); 
    } 

    public enum Key implements Config.Key { 
     PROXY_HOST("proxy.host"), 
     PROXY_PORT("proxy.port"); 
     private final String name; 

     Key(String name) { this.name = name; }  
     @Override 
     public String toString() { return name; } 
    } 
} 

E poi iniettare la configurazione nelle classi:

public class SomeClass { 
    private final Config config; 

    public SomeClass(Config config) { 
     this.config = config; 
    } 

    public void someMethod() { 
     String host = config.get(Key.PROXY_HOST); 
     String port = config.get(Key.PROXY_PORT, "8080"); 
     // Do something 
    } 
}