La Simplicidad viene de la Reducción

La Simplicidad viene de la Reducción
Autor: Paul W. Homer

“Hazlo de nuevo…”, me dijo el jefe mientras su dedo presionaba con fuerza la tecla de borrado. Miré la pantalla de la computadora con una sensación de vacío muy familiar, mientras mi código –línea tras línea– desparecía en el olvido.

Mi jefe, Stefan, no siempre fue el más vocal de las personas, pero él sabía que era un mal código cuando lo veía. Y sabía exactamente qué hacer con él.

Había llegado a mi puesto actual como un programador estudiante con mucha energía, mucho entusiasmo y sin la menor idea de cómo codificar. Tenía esa horrible tendencia a pensar que la solución a cada problema era agregar otra variable en algún lugar. O escribir otra línea. En un mal día, en vez de que la lógica fuera haciéndose mejor con cada revisión, mi código se hacía gradualmente más grande, más complejo y mucho más lejos del trabajo consistente.

Es natural, sobre todo cuando estás apresurado, que sólo quieres hacer los menores cambios a un bloque de código existente, aunque sea horrible. Muchos programadores preservan mal código, temen que iniciar de nuevo requerirá mucho más esfuerzo que continuar donde se quedaron. Esto puede ser cierto para el código que está cerca de ser funcional, pero hay algunos códigos que están más allá de toda ayuda.

Se desperdicia más tiempo en tratar de salvar un mal código del que se debería. Una vez que algo se vuelve un sumidero de recursos, necesita ser descartado. Rápidamente

No es que debas tirar todo lo que has escrito, nombrado y formateado tan fácilmente. La reacción de mi jefe fue extrema, pero me obligó a repensar el código en el segundo (u ocasionalmente tercer) intento. Aún así, la mejor estrategia para arreglar un mal código es cambiándolo de tal modo que el código sea refactorizado sin misericordia, cambiado de lugar o borrado.

El código debería ser simple. Debería ser un mínimo de variables, funciones, declaraciones y otras necesidades sintácticas del lenguaje. Las líneas, variables adicionales… nada de adicional, en realidad, eso debería ser purgado. Removido inmediatamente. Lo que está ahí, lo que queda, sólo debería ser lo suficiente para realizar el trabajo, completar el algoritmo o realizar los cálculos. Cualquier otra cosa y todo lo demás es sólo ruido adicional no deseado, introducido accidentalmente y que obscurece el flujo. Ocultando las cosas importantes.

Por supuesto, si no lo logra, entonces sólo borra todo y escríbelo una vez más. Iniciar el diseño desde lo recordado a menudo puede ayudar a cortar una gran cantidad de desorden innecesario.

Leer contribución original

Sólo el código dice la verdad

Sólo el código dice la verdad
Autor: Peter Sommerlad

La semántica final de un programa está dada por el código que se ejecuta. ¡Si esto es únicamente en formato binario, será una lectura difícil! El código fuente debe, sin embargo, estar disponible si se trata de tu programa, cualquier desarrollo de software comercial típico, un proyecto de software libre o código en un lenguaje interpretado de forma dinámica. Al mirar el código fuente, el significado del programa debería ser evidente. Para saber qué hace el programa, el código es, en última instancia, de lo que puedes estar seguro. Hasta el documento de requisitos más preciso no dice toda la verdad: no contiene el relato detallado de lo que el programa está haciendo, sólo las intenciones de más alto nivel del analista de requerimientos. Un documento de diseño podría capturar un diseño planeado, pero carece del nivel necesario de detalle de la implementación. Estos documento pueden perder sincronía con la implementación actual… o simplemente se han perdido. O nunca fueron escritos, en primer lugar. El código fuente puede ser lo único que queda.

Con esto en mente, pregúntate: ¿qué tan claro es tu código al decirte a ti o a cualquier otro programador qué es lo que está haciendo?

Podrías decir: “Oh, mis comentarios te dirán todo lo que necesitas saber”. Pero recuerda que los comentarios no son código en ejecución. Pueden ser tan malos como cualquier otra forma de documentación. Existe una tradición que dice que los comentarios son incondicionalmente algo bueno, así que algunos programadores escriben más y más comentarios, incluso reiniciando y explicando trivialidades que son obvias en el código. Ésa es la forma errónea de clarificar tu código. Si tu código tiene comentarios, considera refactorizar para que no los tenga. Los comentarios extensos pueden saturar el espacio en la pantalla e incluso pueden ser ocultados automáticamente por tu IDE. Si necesitas explicar un cambio, hazlo en el mensaje de confirmación del sistema de control de versiones, no en el código.

¿Qué se puede hacer para hacer que tu código diga la verdad lo más claro posible? Lucha por buenos nombres. Estructura tu código con respecto a la funcionalidad cohesiva, que también facilita la nomenclatura. Desacopla el código para conseguir ortogonalidad. Escribe pruebas automatizadas explicando el comportamiento previsto y comprueba las interfaces. Refactoriza sin piedad cuando aprendas cómo codificar una solución mejor y más sencilla. Haz que tu código sea tan sencillo como sea posible para leer y entender.

Trata a tu código como a cualquier otra composición, como un poema, un ensayo, un blog público o un email importante. Elabora lo que expresas con cuidado, de modo que haga lo que debe y comunique tan directamente como sea posible lo que está haciendo, para que comunique tus intenciones cuando no estés. Recuerda que el código útil se usa mucho más tiempo de lo previsto. Los programadores de mantenimiento te lo agradecerán. Y, si eres un programador de mantenimiento y el código en el que estás trabajando no dice la verdad fácilmente, aplica las directrices anteriores de manera proactiva. Establece algo de cordura en el código y mantén tu propia cordura.

Leer contribución original

Suelta el ratón y aléjate del teclado

Suelta el ratón y aléjate del teclado
Autor: Cay Horstmann

Te has enfocado por horas en algún raro problema y no hay solución a la vista. Así que te levantas para estirar las piernas o para llegar a la máquina expendedora y, en el camino de vuelta, la respuesta repentinamente se vuelve evidente.

¿Te suena familiar este escenario? ¿Alguna vez te preguntaste por qué sucede? El truco está en que mientras estás codificando, la parte lógica de tu cerebro está activa y el lado creativo se bloquea. No puede presentarte nada hasta que tu lado lógico tome un descanso.

Aquí está un ejemplo de la vida real: estaba limpiando un código heredado y me encontré con un método “interesante”. Estaba diseñado para verificar que una cadena contenía una hora válida usando el formato hh:mm:ss xx, donde hh representa la hora, mm representa los minutos, ssrepresenta segundos y xx podría ser AM o PM.

El método utilizaba el siguiente código para convertir dos caracteres (representando la hora) en un número y verificando que estuviera en el rango adecuado: :

try {
    Integer.parseInt(time.substring(0, 2));
} catch (Exception x) {
    return false;
}

if (Integer.parseInt(time.substring(0, 2)) > 12) {
    return false;
}

El mismo código aparecía dos veces más, con cambios apropiados para el carácter y el límite superior, para poner a prueba los minutos y segundos. El método terminaba con estas líneas para comprobar AM y PM.

if (!time.substring(9, 11).equals("AM") &
    !time.substring(9, 11).equals("PM")) {
    return false;
}

Si ninguna de esta serie de comparaciones fallaba, regresando false, el método regresaba true.

Si el código anterior se ve confuso y difícil de seguir, no te preocupes. Yo también lo creía, lo que significaba que había encontrado algo digno de limpieza. Lo refactoricé y escribí unas cuantas pruebas unitarias, sólo para estar seguro de que aún funcionaba

Cuando terminé, me sentía satisfecho con el resultado. La nueva versión era fácil de leer, de la mitad del tamaño y más precisa debido a que el código original sólo probaba los límites superiores de las horas, minutos y segundos.

Mientras me preparaba para trabajar al día siguiente, una idea surgió en mi cabeza: ¿por qué no validar la cadena usando una expresión regular? Después de unos minutos escribiendo, tenía una implementación funcional de sólo una línea de código. Aquí está:

public static boolean validateTime(String time) {
    return time.matches("(0[1-9]|1[0-2]):[0-5][0-9]:[0-5][0-9] ([AP]M)");
}

El punto de esta historia no es que eventualmente reemplacé cerca de 30 líneas de código con sólo una. El punto es que hasta que me alejé de la computadora pensaba que mi primer intento era la mejor solución al problema.

Así que la próxima vez que estés ante un problema desagradable, hazte un favor: una vez que realmente entiendas el problema ve a hacer algo que involucre el lado creativo de tu cerebro; esboza el problema, escucha algo de música o da un paseo al aire libre. A veces la mejor cosa que puedes hacer para resolver un problema es soltar el ratón y alejarte del teclado.

Leer contribución original

Noticias raras – Los testers son tus amigos

Noticias raras – Los testers son tus amigos
Autor: Burk Hufnagel

Ya sea que se llamen ellos mismos Aseguramiento de Calidad (QC, Quality Check) o Control de Calidad, muchos programadores los llaman problemas. En mi experiencia, los programadores tienen frecuentemente una relación de confrontación con la gente que prueba su software. “Son demasiado exigentes” y “quieren todo perfecto” son las quejas comunes. ¿Te suena familiar?

No estoy seguro del porqué, pero siempre he tenido una visión diferente de los testers. Quizás es porque el “tester” en mi primer trabajo era la secretaria de la empresa. Margaret era una señora muy agradable que mantenía la oficina funcionando e intentaba enseñar a un par de jóvenes programadores cómo comportarse profesionalmente frente a los clientes. Ella también tenía el don de encontrar cualquier error, no importa lo oscuro, en cuestión de minutos.

En ese entonces estaba trabajando en un programa escrito por un contador que pensaba que era un programador. No es necesario decirlo, tenía algunos problemas serios. Cuando pensaba que tenía una pieza sólida, Margaret intentaría usarlo y, más frecuentemente que nunca, fallaría en alguna forma justo después de algunos teclazos. A veces era frustrante y embarazoso, pero ella era una persona agradable a quien nunca pensé en culpar por hacerme ver mal. Eventualmente llegó el día cuando Margaret fue capaz de iniciar limpiamente el programa, introducir una factura, imprimirla y cerrarlo. Estaba muy emocionado. Aún mejor, cuando lo instalamos en una de las computadoras de nuestros clientes, todo funcionaba. Ellos nunca vieron ningún problema porque Margaret me había ayudado a encontrarlos y arreglarlos primero.

Es por eso que digo que los testers son tus amigos. Puedes pensar que te hacen ver mal al reportar cuestiones triviales. Pero cuando los clientes están emocionados por no ser molestados con todas esas “pequeñas cosas” que QC te hizo corregir, entonces te verás bien. ¿Ves a lo que me refiero?

Imagínate esto: estás revisando una utilería que usa “los más prometedores algoritmos de inteligencia artificial” para encontrar y solucionar problemas de concurrencia. Lo inicias e inmediatamente notas que han escrito mal “inteligencia” en la pantalla de inicio. Un poco optimista, pensarás: es sólo un error de dedo, ¿verdad? Entonces notas que la pantalla de configuración usa varias casillas que deberían ser botones de radio y algunos de los atajos de teclado no funcionan. Ahora bien, ninguno de estos son un gran problema, pero conforme los errores se van sumando empiezas a preguntarte sobre los programadores. Si no pueden tener las cosas sencillas bien, ¿cuáles son las probabilidades de que su IA pueda realmente encontrar y solucionar algo tan complicado como los problemas de concurrencia?

Puede que sean genios quienes estaban tan enfocados a hacer la IA increíblemente mejor como para no notar esas pequeñas cosas triviales. Y sin esos “testers exigentes” apuntando los problemas, terminaste encontrándolos. Ahora te estás cuestionando la competencia de los programadores.

Así que por extraño que suene, estos testers, quienes parecen determinados a exponer cada pequeño error en tu código, son realmente tus amigos.

Leer contribución original

Toma ventaja de las herramientas de análisis de código

Toma ventaja de las herramientas de análisis de código
Autor: Sarah Mount

El valor de las pruebas es algo que está siendo inculcado a los desarrolladores de software desde las primeras etapas de su jornada de programación. En años recientes el aumento de las pruebas unitarias, desarrollo basado en pruebas (test-driven), y los métodos ágiles han visto un surgimiento de interés en hacer más pruebas en todas las fases del ciclo de desarrollo. Sin embargo, las pruebas es sólo una de las muchas herramientas que puedes usar para mejorar la calidad del código.

Lejos, en la neblina del tiempo, cuando C era todavía un nuevo fenómeno, el tiempo de CPU y el almacenamiento de cualquier tipo eran un bien escaso. Los primeros compiladores de C eran conscientes de esto, así que reducían el número de pases que hacían a través del código quitando algunos análisis semánticos. Esto significaba que el compilador comprobaba sólo un pequeño subconjunto de errores que podían ser detectados al compilar. Para compensarlo, Stephen Johnson escribió una herramienta llamada lint –la cual remueve la pelusa de tu código– que implementaba algunos de los análisis estáticos que habían sido quitados por el compilador de C. Las herramientas de análisis estático, sin embargo, ganaron la reputación de obtener gran número de advertencias con falsos positivos y avisos sobre convenciones estilísticas que no siempre es necesario seguir.

El panorama actual de los lenguajes, compiladores y herramientas de análisis estático es muy diferente. La memoria y el tiempo de CPU ahora son relativamente baratos, por lo que los compiladores se puede permitir detectar más errores. Casi cualquier lenguaje cuenta con, al menos, una herramienta que comprueba violaciones de estilo, errores comunes y algunos errores astutos que pueden ser difíciles de capturar, tales como potenciales desreferencias de punteros nulos. Las herramientas más sofisticadas, como Split para C o Pylint para Python, son configurables, lo que significa que puedes escoger cuáles errores y advertencias emite la herramienta con un archivo de configuración, a través de la línea de comandos o en tu IDE. Splint incluso te permitirá anotar el código con comentarios que te dará mejores consejos sobre cómo funciona tu programa.

Si todo lo demás falla y te encuentras buscando errores simples o violación de normas que no son capturados por tu compilador, IDE o herramienta lint, entonces siempre puedes llevar tu propio revisor estático. Esto no es tan difícil como suena. La mayoría de los lenguajes, particularmente aquellos etiquetados como dinámicos, exponen su árbol sintaxis abstracto y herramientas de compilación como parte de su biblioteca estándar. Vale la pena saber los rincones polvorientos de la biblioteca estándar que son usados por el equipo de desarrollo del lenguaje que estás usando, ya que frecuentemente contienen gemas ocultas que son útiles para análisis estático y pruebas dinámicas. Por ejemplo, la biblioteca estándar de Python contiene un desensamblador que te dice el código bytecode usado para generar algún código compilado o código objeto. Esto suena como una herramienta obscura para escritores de compiladores en el equipo python-dev, pero es realmente útil para las situaciones diarias. Una cosa que puede desensamblar esta biblioteca es el último trazo de pila (stack trace), dándote una retroalimentación sobre exactamente cuál instrucción de bytecode lanzó la última excepción no capturada.

Así que no dejes que las pruebas sean el final de tu aseguramiento de calidad; toma ventaja de las herramientas de análisis y no tengas miedo de complicarte.

Leer contribución original

Tus clientes no quieren decir lo que dicen

Tus clientes no quieren decir lo que dicen
Autor: Nate Jackson

Nunca he conocido a un cliente que no estuviera muy feliz de decirme qué es lo que quería, usualmente con gran detalle. El problema es que los clientes no siempre dicen toda la verdad. Generalmente no mienten, pero hablan en idioma cliente, no en idioma desarrollador. Usan sus términos y contextos. Dejan fuera detalles importantes. Suponen que has estado en su compañía por 20 años, igual que ellos. ¡Esto se agrava con el hecho de que muchos clientes realmente no saben lo que quieren en primer lugar! Algunos pueden tener un rasgo de la “visión global”, pero rara vez son capaces de comunicar los detalles de sus visiones con efectividad. Otros podrían ser un poco claros en la visión completa, pero saben lo que no quieren. Entonces, ¿cómo es posible que puedas entregar un proyecto de software a alguien que no está diciendo toda la verdad acerca de lo que quiere? Es bastante simple. Sólo interactúa más.

Reta a tus clientes tempranamente y rétalos seguido. No te limites a repetir lo que dijeron que querían en sus palabras. Recuerda: ellos no quieren decir lo que te dijeron. Frecuentemente hago esto intercambiando palabras en conversaciones con ellos y juzgando sus reacciones. Estarás sorprendido de cuántas veces el término cliente tiene un significado completamente diferente al término comprador. Sin embargo, el hombre diciéndote qué quiere en su proyecto de software usará los términos indistintamente y espera que sigas el rastro de a cuál se refiere. Te confundirás y el software que escribas sufrirá.

Discute los temas numerosas veces con tus clientes antes de que decidas que has entendido lo que quieren. Intenta reformular el problema dos o tres veces con ellos. Háblales acerca de las cosas que suceden justo antes o justo después del tópico del que están hablando para obtener un mejor contexto. Si es posible, ten a varias personas hablándote del mismo tema en conversaciones separadas. Casi siempre te dirán historias diferentes, las cuales descubrirán hechos separados pero relacionados. Dos personas hablándote sobre el mismo tema se contradicen frecuentemente. Tu mayor oportunidad de éxito es discutir a fondo las diferencias antes de comenzar la elaboración de tu ultracomplejo software.

Haz uso de ayudas visuales en tus conversaciones. Esto podría ser tan sencillo como usar una pizarra en una reunión, tan fácil como crear un maqueta visual en la fase de diseño o tan complejo como elaborar un prototipo funcional. Es conocido que usar ayudas visuales durante una conversación ayuda a prolongar nuestro periodo de atención e incrementa la tasa de retención de la información. Toma ventaja de este hecho y configura tu proyecto para el éxito.

En una vida anterior era un “programador multimedia” en un equipo que producía proyectos ostentosos. Un cliente nuestro describió sus pensamientos con el look & feel del proyecto con gran detalle. El esquema general de colores discutido en las reuniones de diseño indicaba un fondo negro para la presentación. Pensábamos que lo teníamos hecho. Los equipos de diseñadores gráficos comenzaron a producir cientos de capas de archivos gráficos. Un montón de tiempo fue invertido moldeando el producto final. Una sorprendente revelación fue hecha el día en que mostramos al cliente el fruto de nuestra labor. Al ver el producto, las palabras exactas sobre el color de fondo fueron: “Cuando dije negro, me refería a blanco”. Así que, ya ves, nunca es tan claro como el blanco y negro.

Leer contribución original

Un binario

Un binario
Autor: Steve Freeman

He visto muchos proyectos en los cuales la compilación reescribe alguna parte del código para generar un binario personalizado para cada ambiente destino. Esto siempre hace las cosas más complicadas de lo que deberían ser, e introduce el riesgo de que el equipo podría no tener versiones consistentes en cada instalación. Como mínimo involucra la compilación de múltiples, casi idénticas copias de software, cada una tiene que ser desplegada en el lugar correcto. Significa más partes movibles de lo necesario, lo que significa más oportunidad de cometer un error.

Una vez trabajé en un equipo en el cual cada cambio tenía que ser revisado en cada ciclo de compilación, por los que los testers se quedaban esperando cada que se necesitaba un ajuste menor (¿mencioné que la compilación tomaba también mucho tiempo?). También trabajé en un equipo en el que los administradores de sistemas insistían en reconstruir desde cero en producción (usando el mismo script que hicimos), lo que significaba que no teníamos pruebas de que la versión en producción era la misma que había estado bajo prueba. Y así por el estilo.

La regla es sencilla: compila un sólo binario que puedas identificar y promover a través de todas las etapas en la línea de liberación. Mantén detalles específicos del entorno en el ambiente. Esto podría significar, por ejemplo, mantenerlos en el contenedor de componentes, en un archivo conocido o en la ruta.

Si tu equipo tiene un revoltijo de código para compilar o almacenar todas las configuraciones destino en el código, esto sugiere que nadie ha pensado el diseño con el suficiente cuidado para separar estas características, que son fundamentales de la aplicación, de aquellas que son específicas de las plataforma. O podría ser peor: el equipo sabe qué hacer, pero no puede priorizar el esfuerzo para hacer el cambio.

Por supuesto, hay excepciones: podrías estar compilando para algún destino que tiene importantes restricciones de recursos, pero esto no aplica para la mayoría de nosotros que estamos escribiendo aplicaciones de “bases de datos a pantalla y de regreso”. Alternativamente, podrías estar viviendo con algún desorden heredado que es muy difícil de corregir ahora mismo. En tales casos, tienes que mover gradualmente, pero empezar tan pronto como sea posible.

Leer contribución original

Usa el algoritmo y estructura de datos correctos

Usa el algoritmo y estructura de datos correctos
Autor: JC van Winkel

Un gran banco con muchas sucursales se quejó de que las nuevas computadoras que había comprado para los cajeros eran muy lentas. Esto era antes de que todos usaran la banca electrónica y los cajeros automáticos no estaban tan extendidos como lo están ahora. La gente visitaba el banco mucho más frecuentemente y se hacían largas filas debido a las computadoras lentas. En consecuencia, el banco amenazó con romper su contrato con el proveedor.

El proveedor envió un especialista en análisis y tuning para determinar la causa de los retrasos. Pronto encontró un programa específico ejecutándose en la terminal consumiendo casi toda la capacidad del CPU. Usando una herramienta de perfilado se enfocó en el programa y pudo ver la función culpable. El código se leía:

for (i=0; i<strlen(s); ++i) {
  if (... s[i] ...) ...
}

La cadena s tenía, en promedio, miles de caracteres de longitud. El código (escrito por el banco) fue rápidamente cambiado y los cajeros vivieron felices por siempre…

¿No debía el programador haberlo hecho mejor que un código que innecesariamente escalaba cuadráticamente?

Cada llamada a strlen recorría cada uno de los miles de caracteres en la cadena para encontrar su carácter de terminación nula. La cadena, sin embargo, nunca cambiaba. Al determinar su longitud por adelantado, el programador podía haber ahorrado cientos de llamadas a strlen(y millones de ejecuciones del bucle):

n=strlen(s);
for (i=0; i<n; ++i) {
  if (... s[i] ...) ...

}

Todos conocen el viejo dicho “primero haz que funcione, luego haz que funcione rápido” para evitar las trampas de la micro-optimización. Pero el ejemplo de arriba casi nos hace creer que el programador siguió el maquiavélico adagio “primero haz que funcione lentamente”.

Este tipo de descuido es algo con lo podrías cruzarte más de una vez. Y no es sólo un “no reinventes la rueda”. Algunas veces los programadores novatos sólo empiezan a escribir sin realmente pensar y de repente han “inventado” el ordenamiento por burbuja. Incluso podrían estar alardeando sobre eso.

El otro lado de elegir el algoritmo correcto es la elección de la estructura de datos. Puede hacer una gran diferencia: usar una lista enlazada para una colección de millones de elementos por las que quieres buscar –comparada con una estructura de datos de hash– va a tener un gran impacto en la apreciación del usuario de tu programación.

Los programadores no deberían reinventar la rueda y deberían usar bibliotecas existente cuando fuera posible. Pero, para ser capaces de evitar problemas como el del banco, deberían también ser educados acerca de los algoritmos y cómo escalan. ¿Es sólo la vistosidad en los editores lo que hace que sean tan lentos como los anticuados programas como WordStar en la década de los ochenta? Muchos dicen que el reúso en la programación es de gran importancia. Por encima de todo, sin embargo, los programadores deben saber cuándo, qué y cómo reutilizar. Para poder hacer eso deben tener el dominio del problema y los algoritmos y estructuras de datos.

Un buen programador debería también saber cuándo usar un algoritmo abominable. Por ejemplo, si el dominio del problema dicta que nunca puede haber más de cinco elementos (como el número del dado en el juego Yahtzee) y sabes que siempre tendrás que ordenar, al menos, cinco elementos. En este caso, el ordenamiento por burbuja puede ser la más eficiente forma de ordenar los elementos. Cada perro tiene su día.

Entonces, lee algunos buenos libros y asegúrate de que los entiendas. Si realmente lees bien El arte de la programación, de Donald Knuth, podrías incluso ser afortunado: encuentra una equivocación del autor y gana uno de los cheques de dólares hexadecimales ($2.56).

Leer contribución original

El WET dispersa los cuellos de botella en el rendimiento

El WET dispersa los cuellos de botella en el rendimiento
Autor: Kirk Pepperdine

La importancia del principio DRY es que codifica la idea de que cada pieza del conocimiento en un sistema debería tener una representación única. En otras palabras, el conocimiento debería estar contenido en una implementación única. La antítesis de DRY es WET (Write Every Time, escríbelo todas las veces). Nuestro código es WET cuando el conocimiento es codificado en varias distintas implementaciones. Las implicaciones de rendimiento de DRY versus WET quedan claras cuando consideras los numerosos efectos en un perfil de rendimiento.

Comenzamos considerando una característica en nuestro sistema, digamos X, que es un cuello de botella de CPU. Digamos que la característica X consume el 30% del CPU. Ahora digamos que la característica X tiene diez diferentes implementaciones. En promedio, cada implementación consume 3% del CPU. En este nivel de uso de CPU no es útil preocuparse si estamos buscando una victoria rápida, es común que olvidemos que esta característica es nuestro cuello de botella. Sin embargo, digamos que, de alguna manera, reconocimos la característica X como un cuello de botella. Ahora estamos con el problema de encontrar un arreglo en cada implementación. Con WET tenemos diez diferentes implementaciones que necesitamos buscar y reparar. Con DRY veríamos claramente el 30% de uso de CPU y tendríamos una décima parte de código que arreglar. ¿Mencioné que no tenemos tiempo que perder buscando cada implementación?

Hay un caso de uso en el cual frecuentemente nos sentimos culpables de violar el principio DRY: nuestro uso de colecciones. Una técnica común de implementar una consulta sería el iterar sobre una colección y entonces aplicar la consulta para cada elemento:

public class UsageExample {
    private ArrayList<Customer> allCustomers = new ArrayList<Customer>();
    // ...
    public ArrayList<Customer> findCustomersThatSpendAtLeast(Money amount) {
        ArrayList<Customer> customersOfInterest = new ArrayList<Customer>();
        for (Customer customer: allCustomers) {
            if (customer.spendsAtLeast(amount))
               customersOfInterest.add(customer);
        }
        return customersOfInterest;
    }
}

Al exponer esta colección en bruto a los clientes, hemos violado la encapsulación. Esto no sólo limita nuestra habilidad para refactorizar, obliga a los usuarios de nuestro código a violar el principio DRY al tener cada uno de ellos que reimplementar potencialmente la misma consulta. Esta situación se puede evitar fácilmente al quitar la colección en bruto del API. En este ejemplo podemos introducir un nuevo tipo de colección de dominio específico llamado CustomerList. Esta nueva clase es más semántica en la línea de nuestro dominio. Actuará como una casa natural para todas nuestras consultas.

Tener esta nueva colección nos permitirá ver de forma sencilla si esta consulta es un cuello de botella en el rendimiento. Al incorporar las consultas en la clase eliminamos la necesidad de exponer las elecciones de representación, tales como Arraylist, a nuestros clientes. Esto nos da la libertad de alterar esta implementación sin el miedo de violar los contratos de los clientes:

public class CustomerList {
    private ArrayList<Customer> customers = new ArrayList<Customer>();
    private SortedList<Customer> customersSortedBySpendingLevel = new SortedList<Customer>();
    // ...
    public CustomerList findCustomersThatSpendAtLeast(Money amount) {
        return new CustomerList(customersSortedBySpendingLevel.elementsLargerThan(amount));
    }
}

public class UsageExample {
    public static void main(String[] args) {
        CustomerList customers = new CustomerList();
        // ...
        CustomerList customersOfInterest = customers.findCustomersThatSpendAtLeast(someMinimalAmount);
        // ...
    }
}

En este ejemplo, la adherencia a DRY nos permite introducir un esquema de índice alterno con SortedList usando una llave en el nivel de gasto de nuestros clientes. Más importante que los detalles específicos de este ejemplo, en particular, seguir el principio DRY nos ayuda a encontrar y reparar cuellos de botella en el rendimiento que habrían sido más difíciles de encontrar si el código fuera WET.

Leer contribución original