Java Map反模式?

6

编辑:我已经收到了几个回答,它们在问题中已经提到了。我真正感兴趣的是寻找相关的参考资料。


我正在查看一个代码示例,它基本上遵循这种模式:

Map<String, List> getListsFromTheDB() {
  Map<String, List> lists = new HashMap<String, List>();

  //each list contains a different type of object
  lists.put("xList", queryForListOfXItems());
  lists.put("yList", queryForListOfYItems());

  return lists;
}

void updateLists() {
  Map<String, List> lists = getListsFromTheDB();
  doSomethingWith(lists.get("xList"));
  doSomethingWith(lists.get("yList"));
}

我认为这是一种反模式。程序员应该创建一个可以返回的类,像这样:

class Result {
  private final List<X> xList;
  private final List<Y> yList;

  public Result(xList, yList) {
    this.xList = xList;
    this.yList = yList;
  }

  public List<X> getXList() { xList; }
  public List<Y> getYList() { return yList; }
}

这样做会更加类型安全,避免过度泛化一个非常具体的问题,并且在运行时更不容易出错。

有没有人能指向任何权威的参考资料,说明应该避免这种模式?或者,如果这是一个好的模式,请给出理由。


1
注意,始终返回两种类型的列表;它不会因使用而变化。 - RMorrisey
你所说的在特定情况下是合理的,因为你总是知道doSomething的输入是xList和yList。但是,如果你只想批量处理返回的列表而不需要引用每个单独的列表,那么返回一种Map/List/Set结构确实是有意义的。 - Strelok
我认为那是一种反模式。 - ColinD
4个回答

4

我认为这取决于上下文。

如果你返回一个map,调用者必须知道'xList'和'yList'这些“魔法”键来从map中获取实际数据。我所说的魔法是指魔法常量。(你可以迭代map来查找魔法键,但那只是一个技巧。)通过使用map,你实际上隐藏了数据,使得获取想要的内容(x-和yLists)更加困难。

这些魔法常量不一定非得是如此神秘。如果'xList'和'yList'是数据库表名(或任何外部字符串),那么我希望得到一个从表名到对象列表的映射。有人可能会添加/重命名/删除表格。(或者,也许更漂亮的是,我希望能够按表查询,比如getListFromTheDB("xList");。)

在你的代码中,你有这个方法

queryForListOfXItems();

这似乎是硬编码的xList和yList。因此(在我看来),选择地图会是一个不好的选择。


编辑了问题以澄清我正在寻找什么。 - RMorrisey
@RMorrisey - 魔数(或常量)是一种反模式,因此这种风格需要一个。但我不知道是否存在“映射反模式”。也许现在有了 ;) - Ishtar

4

我认为关键点在于列表的数量是固定的。既然你确定代码只使用了2个列表,那么使用map就有点过于概括了。

所以,我认为 'class Result' 更好。


2

我同意你的观点。显然,这个人很懒,使用Map来避免创建新类。副作用是需要使用getListsFromTheDB()的代码将变得不太可读,并且,正如你所提到的那样,更容易出错。

当然,还有一种选择,即调用者创建列表:

void fillFromTheDB(List<X> xList, List<Y> yList) {
  //each list contains a different type of object
  xList.addAll(queryForListOfXItems());
  yList.addAll(queryForListOfYItems());
}

void updateLists() {
  List<X> xList = new ArrayList<X>();
  List<Y> yList = new ArrayList<Y>();
  fillFromTheDB(xList, yList);
  doSomethingWith(xList);
  doSomethingWith(yList);
}

编辑了问题以澄清我正在寻找什么。 - RMorrisey

1

我没有任何权威的资料,但我的直觉告诉我,除非在实际代码中发生了更复杂的事情,否则以这种方式使用Map对我来说并不麻烦。实际上,Result类感觉有点过度设计。


2
编译器无法捕获您输入“xlist”而不是“xList”的错误,这难道不让你感到困扰吗?在我的实际代码中,这两个方法分别位于不同的类和不同的包中。您可以通过使用常量来解决此问题,但是,为什么要一开始就引入潜在的问题呢? - RMorrisey
3
我想它有一点作用。再深入考虑一下,我更愿意只看到两种方法,getXListFromTheDB()和getYListFromTheDB()。 - Matt McHenry
我同意在这个例子中使用两个单独的方法会更加清晰,但我认为那不是重点。=) - RMorrisey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接