当KISS和DRY碰撞时

时间:2011-09-05 11:12:53

标签: java dry

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

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

  1. 迭代A类型列表的元素
  2. 将类型A的元素转换为类型B并将它们插入到类型B
  3. 的列表中

    以下是一个例子:

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

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

    所以我们有:

    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-Schema生成)。

5 个答案:

答案 0 :(得分:15)

要么没事。

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

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

答案 1 :(得分:12)

如果您只需要在整个应用程序中执行此操作4次,并且转换实际上与示例相同,我会选择在通用解决方案上随时编写4 for循环。

可读性因使用该通用解决方案而受到很大影响,而您实际上并没有从中获得任何东西。

答案 2 :(得分:10)

像DRY和KISS这样的一般原则从来都不会

IMO,答案是忘记教条(至少对于这个问题),并考虑什么能为您提供最佳/最具可读性的解决方案。

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

(Thilo的答案也是对的...... IMO)

答案 3 :(得分:4)

我认为不是KISS和DRY相互矛盾。我宁愿说Java不允许你表达简洁而不重复自己。

首先,如果你引入了正确命名的方法,从List<A>转换为List<B>等等,而不是一直重复循环,它仍然是干的,同时仍然保持KISS。

但我建议的是看一下替代语言,这些语言可以让你在继续推广KISS的同时充分利用DRY,例如:在斯卡拉:

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

其中convertAtoB是一个采用A类项目并返回B的函数:

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

或者您甚至可以链接这些map来电。

答案 4 :(得分:4)

您可以将转换功能移动到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()));