静态初始化块的设计问题

时间:2012-08-06 19:24:19

标签: java static

我有一个设计问题:让我以简单的例子来表达:

Public class A()
{
public static HashMap map = new HashMap();
public static String url = "default";
static {
  getJson();
}

//url getters and setters are defined

public static getJson() {
//code which uses url to get json and populate hashmap
}
public string getresult(String key) {
//uses hashmap to send result.
}

我正在使用静态初始化块,因为我想只获取一次json。

public class B {

//这里我想更改url并调用getJson方法。如果我在设置url之前调用A.setUrl(),则调用A.getJson()方法,因为它在静态初始化块中。我可以先设置url然后调用getJson()。

//is this a bad design?

}

3 个答案:

答案 0 :(得分:2)

这应该可行。添加新方法。

public static void getJson(String url) {
setUrl(url);
getJSon();
}

静态初始化器通常是一个坏主意,因为单元测试变得困难。

查看Misko Hevery's Guide to writing Testable Code

您可以通过以下方式重新设计设计:

public class A {
  //Add generics
  private Map map = new HashMap();
  public A(Map map){
    this.map = map;
  }
  public String getresult(String key) {
  //uses hashmap to send result.
  }

}

//Helper Class
public class URLToJSon() {
//Add private constructor
  public static Map convertUrlToJSon(String url) {
   //do the conversion and return a hashmap
  }
}

通过这种方式,我们可以遵循Single Responsibility Principle

现在这两个类都是可测试的。

答案 1 :(得分:2)

是的,设计很糟糕:

  1. 在不修改A的定义的情况下,无法自定义A从哪里获取数据。除此之外,这还可以防止单元测试(因为如果网络不可用,您可能不希望单元测试失败... )。
  2. 如果初始化失败(例如因为远程URL当前不可用),则无法轻易捕获该异常,因为您不知道哪个访问触发了加载。您不能从静态初始化程序中抛出已检查的异常。您也无法重试初始化(所有后续访问都会立即导致异常)。
  3. 如果您必须通过静态字段访问A,我建议:

    public class A {
        private static Map<String, String> map;
    
        /** must be invoked before get is first called */
        public static void init(Map<String, String> newmap) {
            map = newmap;
        }
    
        public static String get(String key) {
            return map.get(key);
        }
    }
    

    这就分散了使用数据和获取数据的担忧,允许每个数据被独立替换和测试。

    还要考虑摆脱static,因为它强制执行整个应用程序中只有一个地图同时存在,这是非常不灵活的。 (参见Ajay关于如何回答的第二个代码示例)

答案 2 :(得分:1)

网址设置在哪里?在构造函数中?如果是这样,只需执行

//Normal init stuff like set url here, followed by
if (! some check for if json is set) {
     setJson();
}