2011-09-05 8 views
15

soy un seguidor obsesivo de los DRY y KISS principios pero la semana pasada tuve un caso en el que ambos parecen contradecirse entre sí:Cuando KISS y SECO chocan

Para una aplicación que estaba haciendo, tenía que poner en práctica un bucle de veces que hace lo siguiente:

  1. iterar sobre los elementos de una lista de tipo a
  2. convertir el elemento de tipo a a B e insertarlos en una lista de tipo B

He aquí un ejemplo:

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

Dentro del código, que tengo que hacer esto unas 4 veces, convertir un tipo (por ejemplo, D, E etc.) en otro. Es posible que no pueda cambiar los tipos que voy a convertir, ya que son tipos de terceros que debemos usar en nuestra aplicación.

Así tenemos:

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

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

... 

Por lo tanto, no violar seca, se me ocurrió una solución genérica:

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)); 
    } 
} 

Una llamada se ve algo como esto:

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

Ahora, aunque esto es mejor en términos de DRY, creo que infringe KISS, ya que esta solución es mucho más difícil de comprender y que los bucles duplicados.

Entonces, ¿esto es DRY vs. KISS? ¿Cuál a favor en este contexto?

EDITAR

Para que quede claro, la clase que estoy hablando es de un adaptador, que delega llamada a un sistema heredado de nuestra propia aplicación, convertir el legado en nuestros propios tipos a lo largo del camino. No tengo medios para cambiar los tipos heredados, ni puedo cambiar nuestros tipos (que son generados por XML-Schema).

+7

palabras clave: java beso seco - me encanta! – hawkeye

+1

¿Qué significa "seco"? Ah, la respuesta lo dice, no te repitas, supongo. –

+0

@Angel, sry Agregaré referencias más adelante :-). – helpermethod

Respuesta

15

Cualquiera está bien.

Con los bucles, en realidad no se está repitiendo, porque las únicas partes que son repetitivas son "desorden sintáctico" (y no mucho de eso en su caso). No está repitiendo/duplicando el código de "lógica de aplicación".

Si le gusta el estilo de "Función", tal vez haga uso de la biblioteca Guava (que tiene la interfaz de Función y muchos métodos de ayuda que trabajan con ellos en las colecciones). Eso es DRY (porque no te repites y reutilizas el código que ya existe), y aún KISS (porque esos patrones son bien entendidos).

+0

+1 Thx, olvídate totalmente de la guayaba! – helpermethod

9

Principios generales como DRY y KISS nunca funcionan todo el tiempo.

IMO, la respuesta es olvidar el dogma (al menos para este problema), y pensar qué es lo que te ofrece la mejor solución o la más fácil de leer.

SI el código x 4 duplicada es más fácil de entender y no una carga de mantenimiento (`cos necesita cambiar mucho), es la solución correcta.

(Y la respuesta de Thilo es correcto también ... OMI)

12

Si sólo tienes que hacer esto 4 veces en toda su aplicación, y la conversión es realmente tan trivial como sus ejemplos, elegiría escribir 4 para bucles en cualquier momento sobre la solución genérica.

La legibilidad sufre mucho por usar esa solución genérica y en realidad no obtienes nada de ella.

+4

No gana mucho de DRY en este caso: no tiene una factorización de código importante; es probable que no tenga un comportamiento incoherente introducido al repetirse. Todavía se repite bastante porque tiene que escribir "nueva Función {X aplicar (Y y) {devolver CFactory.convertFromYToX (y); }}" todo el tiempo. ME BESARÍA esta vez. – ysdx

4

Creo que no es que KISS y DRY se contradicen entre sí. Prefiero decir que Java no te permite expresar simplicidad mientras no te repites.

En primer lugar, si introduce métodos correctamente nombrados para convertir de List<A> a List<B> y así sucesivamente, en lugar de repetir el ciclo todo el tiempo, sería SECO mientras permanezca KISS.

Pero lo que yo aconsejaría es buscar idiomas alternativos que le permitan aprovechar al máximo DRY mientras promociona KISS, p. en Scala:

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

Dónde convertAtoB es una función de tomar un elemento de tipo A y B de regresar:

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

O incluso se puede encadenar estos map llamadas.

4

podría mover la función de conversión en CFactory:

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

El código es bastante legible/sencilla de esta manera y que promueven la reutilización de código (tal vez usted tendrá que utilizar el objeto convertidor más tarde en otro contexto).

Implementación:

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

(usando iteradores de guayaba).

Ni siquiera estoy seguro de que debe secarse aquí, se puede utilizar directamente:

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

+1 Esta es una buena idea :-). – helpermethod

Cuestiones relacionadas