I'm an obsessive follower of the DRY and KISS principles but last week I had a case where both seem to contradict each other:
For an application I was doing, I had to implement a loop for times which does the following:
- iterate over the elements of a list of type A
- convert the element of type A to type B and insert them into a list of type B
Here's an example:
for (A a : listOfA) {
listOfB.add(BFactory.convertFromAToB(a));
}
Within the code, I have to do this about 4 times, convert a type (e.g. D, E etc.) into another one. I may not be able to change the types I'm about to convert, as they are 3rd party types which we have to use in out app.
So we have:
for (A a : listOfA) {
listOfB.add(BFactory.convertFromAToB(a));
}
for (C a : listOfC) {
listOfB.add(DFactory.convertFromCToD(c));
}
...
So, to not violate dry, I came up with a generic solution:
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));
}
}
A call looks something like this:
convertAndCopy(listOfA, listOfB, new Function<A, B>() {
A apply(B b) {
return CFactory.convertFromBToC(b);
}
});
Now, while this is better in terms of DRY, I think it violates KISS, as this solution is much harder to understand than the duplicated for loops.
So, is this DRY vs. KISS? Which one to favor in this context?
EDIT
Just to be clear, the class I'm talking about is an Adapter, which delegates call to a legacy system to our own implementation, converting the legacy into our own types along the way. I have no means of changing the legacy types, nor may I change our types (which are XML-Schema-generated).