【问题标题】:When KISS and DRY collide [closed]当 KISS 和 DRY 碰撞时[关闭]
【发布时间】:2011-09-05 11:12:53
【问题描述】:

我是DRYKISS 原则的狂热追随者,但上周我遇到了一个似乎相互矛盾的案例:

对于我正在做的应用程序,我必须实现一个循环执行以下操作:

  1. 遍历 A 类型列表的元素
  2. 将 A 类型的元素转换为 B 类型,并将它们插入到 B 类型的列表中

这是一个例子:

for (A a : listOfA) {
    listOfB.add(BFactory.convertFromAToB(a));
}

在代码中,我必须这样做大约 4 次,将一种类型(例如 D、E 等)转换为另一种类型。我可能无法更改要转换的类型,因为它们是我们必须在应用中使用的第 3 方类型。

所以我们有:

for (A a : listOfA) {
    listOfB.add(BFactory.convertFromAToB(a));
}

for (C a : listOfC) {
    listOfB.add(DFactory.convertFromCToD(c));
}

...

所以,为了不违反干货,我想出了一个通用的解决方案:

private interface Function<S, T> {
  T apply(S s);
}

public <S, T> void convertAndCopy(List<S> src, List<T> dst, Function<S, T> f) {
    for (S s : src) {
      dst.add(f.apply(s));
    }
}

调用看起来像这样:

convertAndCopy(listOfA, listOfB, new Function<A, B>() {
    A apply(B b) {
        return CFactory.convertFromBToC(b);
    }
});

现在,虽然这在 DRY 方面更好,但我认为它违反了KISS,因为这种解决方案比重复的 for 循环更难理解。

那么,这是 DRY vs. KISS 吗?在这种情况下,应该支持哪一个?

编辑

为了清楚起见,我正在谈论的类是一个适配器,它将对遗留系统的调用委托给我们自己的实现,并在此过程中将遗留系统转换为我们自己的类型。我无法更改遗留类型,也无法更改我们的类型(由 XML 模式生成)。

【问题讨论】:

  • 关键字:java 干吻 - 爱它!
  • “干”是什么意思?啊,答案就是这样,我猜不要重复自己。
  • @Angel,对不起,我稍后会添加参考资料 :-)。
  • @helpermethod - 不需要。 Angel 应该只阅读标签的标签 wiki;例如将鼠标悬停在它上面。

标签: java dry


【解决方案1】:

两者都可以。

使用循环,您并没有真正重复自己,因为唯一重复的部分是“语法混乱”(在您的情况下并没有太多)。您不是在重复/复制“应用程序逻辑”代码。

如果你喜欢“Function”风格,可以使用 Guava 库(它有 Function 接口和许多在集合上使用它们的辅助方法)。那是 DRY(因为您不会重复自己,而是重复使用已经存在的代码),并且仍然是 KISS(因为这些是很好理解的模式)。

【讨论】:

  • +1 谢谢,完全忘记番石榴!
【解决方案2】:

如果您只需要在整个应用程序中执行 4 次此操作,并且转换确实像您的示例一样简单,那么我会选择在通用解决方案中随时编写 4 个 for 循环。

使用该通用解决方案会严重影响可读性,而且您实际上并没有从中获得任何好处。

【讨论】:

  • 在这种情况下,您不会从 DRY 中获得太多收益:您没有主要的代码分解;你可能不会因为重复自己而引入不连贯的行为。您仍然会重复自己,因为您必须一直编写“new Function{X apply(Y y){return CFactory.convertFromYToX(y); }}”。这次我会亲吻。
【解决方案3】:

DRY 和 KISS 等一般原则永远不会始终有效

IMO,答案是忘记教条(至少对于这个问题),并考虑什么给你最好/最易读的解决方案。

如果重复的 x 4 代码更容易理解并且它不是维护负担(即您不需要更改很多),那么这是正确的解决方案。

(Thilo 的回答也是对的……IMO)

【讨论】:

    【解决方案4】:

    我认为 KISS 和 DRY 并不矛盾。我宁愿说Java不允许你在不重复自己的情况下表达简单。

    首先,如果您引入正确命名的方法以从 List&lt;A&gt; 转换为 List&lt;B&gt; 等等,而不是一直重复循环,它将是 DRY 而仍然保持 KISS。

    但我的建议是查看其他语言,这些语言可以让您充分利用 DRY,同时仍然推广 KISS,例如在 Scala 中:

    val listOfB = listOfA map convertAtoB
    val listOfC = listOfB map convertBtoC
    val listOfD = listOfC map convertCtoD
    

    convertAtoB 是一个函数,它接受 A 类型的项目并返回 B:

    def convertAtoB(a: A): B = //...
    

    或者您甚至可以链接这些map 调用。

    【讨论】:

      【解决方案5】:

      你可以将转换函数移到 CFactory 中:

      convertAndCopy(listOfA, listOfB, CFactory.getConverterFromAToB());
      

      通过这种方式,代码非常易读/简单,并且您可以促进代码重用(也许您稍后需要在另一个上下文中使用转换器对象)。

      实施:

      public <S, T> void convertAndCopy(List<A> listofA, List<B> listOfB, Function<A, B> f) {
        listOfB.addAll(Collections2.transform(listOfA,f));
      }
      

      (使用番石榴迭代器)。

      我什至不确定你是否应该在这里干燥,你可以直接使用:

      listOfB.addAll(Collections2.transform(listOfA,CFactory.getConverterFromAToB()));
      

      【讨论】:

      • +1 这实际上是个好主意 :-)。
      猜你喜欢
      • 2021-09-27
      • 1970-01-01
      • 2013-01-11
      • 2014-02-04
      • 2018-04-19
      • 1970-01-01
      • 2013-01-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多