2010-03-23 23 views
30

Tôi thường phải đối mặt với vấn đề lưu trữ, trong bộ nhớ, một vài cài đặt cấu hình (có thể phức tạp) được tải từ các tệp trên hệ thống tệp. Tôi tự hỏi nếu có một cách tốt hơn để kiến ​​trúc sư một mô hình cho vấn đề này, tuy nhiên, hơn những gì tôi đã sử dụng.Mẫu thiết kế cho thuộc tính cấu hình được tải một lần?

Về cơ bản, giải pháp hiện tại của tôi bao gồm ba bước.

  1. Tạo một đĩa đơn. Vì dữ liệu liên tục và được bảo đảm không thay đổi thông qua thời gian chạy của ứng dụng, nên chỉ cần một cá thể đối tượng.

  2. Khi yêu cầu đầu tiên cho đối tượng được tạo, hãy tạo đối tượng và đọc từ một tệp.

  3. Hiển thị dữ liệu bằng getters.

này có hiệu quả mà rất nhiều mã của tôi trông như thế này: MyConfiguration.getInstance().getWeightOfBomb(), trông khá lạ với tôi.

Có cách nào tốt hơn để xử lý điều này theo cách ngữ nghĩa hơn không?

Trả lời

29

Tiêm phụ thuộc. Bạn không nhất thiết phải sử dụng một khung DI như Spring hoặc Guice nhưng bạn thực sự muốn tránh xả rác mã của mình bằng các trình đơn. Bạn vẫn có thể sử dụng một singleton trong việc thực hiện, nhưng không có lý do gì mà phần còn lại của mã của bạn cần phải biết rằng đó là một singleton. Singletons là nỗi đau lớn khi kiểm tra đơn vị và tái cấu trúc. Hãy để mã của bạn tham chiếu giao diện thay thế. ví dụ,

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(); 
    } 
} 

Nếu bạn sử dụng một khuôn khổ DI, chỉ cần thiết lập bạn lớp học để có thực hiện MyConfig bạn tiêm. Nếu bạn không, thì cách tiếp cận lười biếng nhất vẫn có tất cả những lợi ích là làm một việc gì đó như:

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

Thực sự điều đó tùy thuộc vào bạn. Điều quan trọng là bạn có thể thay thế myConfig trên cơ sở từng trường hợp khi sau này bạn nhận ra rằng bạn cần hành vi thay đổi và/hoặc cho thử nghiệm đơn vị.

+3

Rất nhiều của thời đại, tôi chỉ cần bao bọc một số thuộc tính cấu hình trong các ứng dụng giao diện điều khiển nhỏ (mà tôi đã viết nhiều, gần đây) . Trong các ứng dụng lớn hơn, mặc dù (mà tôi đã sử dụng mẫu này), phương thức này hoạt động tốt. –

2

Đề xuất bổ sung cho câu trả lời của noah.

Nếu bạn thấy bất tiện khi viết một phương thức cho mỗi thông số cấu hình, thì bạn có thể sử dụng enum cho điều đó. Dưới đây là những gì tôi có nghĩa là:

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()); 
} 

}

Sau đó, nếu bạn có một tham số cấu hình mới, tất cả các bạn cần làm là thêm một mục enum mới.

Bạn cũng có thể trích xuất một giao diện từ lớp Cấu hình, tất nhiên, di chuyển enum ra ngoài.

3

Bạn có thể tạo một giao diện để đại diện cho cấu hình:

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

Và một thực hiện 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; } 
    } 
} 

Và sau đó tiêm các cấu hình trong lớp học của bạn:

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 
    } 
} 
Các vấn đề liên quan