Jun 17

El difícil compromiso entre KISS y OO

 

KISS es la abreviatura de "Keep it simple, stupid" o traducido, "Manténlo simple, estúpido". La idea es que el código debe hacerse siempre lo más sencillo posible. Esto ayuda a que haya menos errores y a que sea más fácilmente mantenible en un futuro, ya que será más fácil entenderlo por terceras personas y corregir posibles errores que queden.

OO es la orientación a objetos. Un código diseñado con orientación a objetos debe usar cosas como clases, herencias, polimorfismos, encapsulación, etc. Tras muchos años de diseño orientado a objetos, se ha llegado a que hay determinadas formas de combinar clases e interfaces para obtener unos resultados óptimos, los llamados patrones de diseño. Como consecuencia, un buen diseño orientado a objetos normalmente implica el uso de determinados patrones de diseño. También, en un buen diseño orientado a objetos, se usan varias clases cada una de ellas con una responsabilidad concreta muy definida, es decir, suelen ser clases pequeñas y que sólo hacen cosas concretas. El fin último de la orientación a objetos es conseguir que el código sea reutilizable. Podemos llevarnos a otros proyectos sólo aquellas clases que realmente necesitamos y además, están hechas de tal manera, que no es necesario modificarlas, sino que nos sirven tal cual.

Pues bien, KISS y OO, hasta cierto punto son opuestos y veamos un pequeño ejemplo tonto. Imagina que vamos a hacer un programa que pida al usuario por pantalla los coeficientes de una ecuación de segundo grado y nos muestre el resultado por pantalla.

Si llevamos KISS a sus últimas consecuencias y tratamos de hacer el código lo más simple posible, sin preocuparnos en absoluto de posible reusabilidad de lo que hacemos, lo más sencillo es hacer una única clase con un método main(). En ese método pedimos por pantalla los coeficientes (podemos hacer un método estático encargado de pedir un número por pantalla), echamos las cuentas (podemos meterlas en otro método estático) y sacamos los resultados (si acaso en otro método estático). Resultado final: una clase sencilla, que hace lo que tiene que hacer, con quizás tres métodos además del main(), pero que no es en absoluto reusable. Hace lo que hace y nada más. Sólo pide coeficientes por pantalla, echa unas cuentas y saca los resultados por pantalla.

Si pensamos más bien en hacer algo reusable y aplicamos orientación a objetos, posiblemente hagamos una clase EcuacionSegundoGrado a la que le pasemos los coeficientes y nos devuelva los resultados. Haremos otra clase (o dos clases) para pedir y sacar cosas por pantalla. Estas clases, idealmente, deberían implementar interfaces de pideDato() y muestraDato(), de forma que si más adelante en vez de pantalla es una base de datos, nos baste con implementar esas interfaces. Finalmente, posiblemente hagamos otra clase más con un main() encargada de instanciar las anteriores y hacerlas funcionar conjuntamente. El resultado final es que tenemos una serie de clases con responsabilidades separadas y reusables. Podemos coger cualquiera de esas clases y llevarla tal cual a otro proyecto y usarla a nuestro gusto.

Pero…. hemos perdido la sencillez. Ojo, no quiero decir que estas clases no estén bien hechas o que sean dfiíciles de entender si están bien hechas, pero donde antes teníamos una única clase y siguiendo el main() lo veíamos todo, ahora tenemos tres o cuatro clases, dos interfaces y seguir el código nos obliga a navegar de una a otra … y navegar a través de una interface no es sencillo, puesto que no sabemos qué implementación hay detrás (en este ejemplo sí sabemos cual hay detrás, porque sólo hay una). Obviamente, todo esto es peor si nuestro programa no es tan tonto como una ecuación de segundo grado.

¿Cual es entonces la situación ideal?. Las metodologías ágiles, en general, nos dan la solución bien clara. Haz el código lo más simple posible sólo para lo que tienes que hacer ahora (la historia de usuario concreta que estás haciendo ahora, el bug que estás corrigiendo, etc).

Si tenemos una historia de usuario que dice que pedimos los coeficientes por pantalla, resolvemos la ecuación y mostramos por pantalla los resultados, debemos, sin dudarlo, hacer la primera implementación, la del KISS puro y duro, tal cual nos aconsejan las metodologías ágiles. Cualquier metodología ágil nos aconseja también a hacer test automáticos de prueba. de forma que si más adelante tenemos que rehacer, tengamos seguridad de no estropear.

Supongamos ahora una segunda historia de usuario que nos dice que los coeficientes se deben leer de base de datos. Bien, este y no otro es el momento de hacer la interface con el método pideCoeficiente(), hacer dos implementaciones, la de pantalla y la de bd, y cambiar nuestro código para que no vaya directamente contra pantalla, sino contra esta interface cuya implementación alguien le pasará de fuera. Al tener test de la primera parte, tendremos seguridad de no haber estropeado lo que ya funcionaba después de tocar y ya habremos empezado nuestro diseño OO, por necesidad, y no por gusto. Ojo, todavía no hemos extraido la clase EcuacionSegundoGrado ni hemos hecho metodo en la interface de muestraResultado(), porque nadie nos ha pedido mostrarlo en otro sitio que no sea pantalla.

Resumiendo, es muy importante para tener un código lo más claro posible, no lanzarse sin ton ni son a hacer clases, interfaces, aplicar patrones y usar a tope la OO. Hay que ser realista y usar esas características de OO sólo cuando es estrictamente necesario o cuando estamos seguros al 99% de que vamos a necesitarlas. En el ejemplo anterior, si tenemos la lista de historias de usuario en algún sitio y vemos que la segunda está justo detrás de la primera, quizás, y en contra de las metodologías ágiles estrictas, podamos ir haciendo la interface ya en la primera implementación.

Entradas relacionadas:

  • No hay entradas relacionadas.

7 Responses to “El difícil compromiso entre KISS y OO”

  1. gimenete Says:

    Creo que el problema es “qué entiendes por simple”? Simple en diseño o simple de extender o simple de mantener o…

    Hacer una clase con un main() es elegir un “diseño simple”, pero no es “simple de extender”. Por lo tanto, qué quiere decir KISS? “Mantenlo simple en diseño” o “Mantenlo simple de extender” o incluso “Mantenlo simple de usar”. Y con esto último (“simple de usar”) no me refiero a la usabilidad, sino al código: por ejemplo un API simple en su interfaz externa puede ser muy compleja en su implentación.

    KISS es algo muy genérico. Puedes mantener simple el diseño o la implementación o… o hacer un equilibrio según las necesidades en cada momento. La OO te permitirá hacer un diseño más simple de extender y de mantener, por lo tanto OO también es KISS 😛

    saludos!

  2. blaxter Says:

    Los patrones de diseño ayudan a mantener las cosas simples, lo que no ayuda es usarlos cuando no se necesitan.

    ps: Una única clase con un método main y unión de lógica del programa con la GUI eso no es KISS, eso es lo más jodidamente retorcido que se puede imaginar.

    ps2: la reutilización de clases es un mito de OO (reutilizar librerías, sí, ¿pero clases individuales? riau, riau).

  3. Shd Says:

    Un artículo interesante, pero discrepo contigo en algunos aspectos. Has orientado las diferencias entre lo que podríamos denominar “KISS” y “POO” a un ejemplo demasiado sencillo, y creo que eso puede inducir a algunos errores.
    En cualquier proyecto real, la complejidad aumenta de forma muy rápida, pese a ello siempre es posible dividir el trabajo en pequeñas funciones, que realicen un trabajo determinado (qué sería de nostros sino). En tal caso, el método “KISS” coincide plenamente con el que tú has indicado como mas complejo: separar las funciones de toma de datos, muestra, calculos internos.. en definitiva el conocido modelo vista-controlador.
    Y esto se puede ver claramente en que cuando tu te enfrentas a un proyecto ya existente pero desconocido para ti, ver clases en las que se mezcla por un lado la toma de datos, por otro su modificación, etc. es un caos. En cambio, si encontramos cada “trabajo” separado, según su aplicación, será mucho mas sencillo entender qué está ocurriendo en cada momento.

    Un saludo y enhorabuena por el blog, que sigo desde hace bastante tiempo y cada vez está mejor.

  4. Chuidiang Says:

    @Gimenete. Entiendo por KISS el hacer el código lo más sencillo posible (entendible y diseño simple) para que cumpla el objetivo previsto (que haga algo concreto o que sea extensible o que sea fácilmente xxx). La OO puede ser o no KISS dependiendo de quién lo haga y cómo lo haga, independiemente del objetivo, no es KISS por sí misma. Evidentemente, si el objetivo del código es ser fácilmente ampliable y modificable, un diseño simple debe ser OO y no algo como un main(). Pero si el objetivo es que se haga algo concreto y no está previsto ser ampliado, quizás la OO es excesiva.

    @blaxter.
    ps: Lo del main() con todo junto es posible en este ejemplo porque es muy tonto. En un problema más grande habría más clases y más separación entre cosas. Pero de la misma forma, si para este ejemplo tonto exagerando la OO hay tres o cuatro clases y dos interfaces, para un problema complejo habría tres miles de clases y dos miles de interfaces. Como comenta Gimente, hay que determinar cual es el objetivo de nuestro código y ser consecuente con él. La OO se ha inventado para hacer código fácilmente reutilizable y ampliable. Si nuestro objetivo no es ese, nos sobra la OO y deberíamos usar lo menos posible.
    ps2:Doy por sobreentendido que para reutilizar clases estas deben estar compiladas y separadas en una librería fuera del proyecto. Si quiero que mi clase EcuacionSegundoGrado sea retulizable, tendré que hacer mi librería matematica.jar y meter ahí esa clase ya compilada.

    Sed buenos.

  5. atreyu Says:

    Bueno hay muchas siglas y principios y como programar no deja de tener alguna pincelada de artesania o incluso arte (o eso me gusta pensar) muchas veces entramos en terrenos de la estetica mas que en el puro blanco y negro de la ciencia o la ingenieria.

    Por mi parte el principio que casi sigo a rajatabla es el DRY (dont repeat yourself), me chirria tanto ver un codigo repetido mas de dos veces que me supera y tengo que montarme algo para evitar una tercera repeticion (y eliminar las dos anteriores de paso). La abstraccion permite eliminar la repeticion de codigo y reusarlo, aunque la oop no es la unica manera de abstraer…en cualquier lisp (en clojure por ejemplo) por ejemplo el tener closures y poder modificar el codigo en tiempo de evaluacion con macros te permite abstraer muchas veces de forma mas rapida, sencilla y expresiva que con la oop y sus a veces rebuscados patrones.

    El problema es cuando antes siquiera de que se repita una vez ya piensas en que tal vez se repita, que tal vez cambie el requirimiento, que tal vez el usuario no sepa lo que quiere, que tal vez…y como cuando haces la maleta la llenas de “por sies” y acabas con el triple de lo que luego usas realmente. Por eso prefiero refactorizar a partir de la segunda repeticion que pensar en las interfaces desde el principio a no ser que este muy muy clara su utilidad.

  6. Julio Quiberre Says:

    Tal vez necesiten leer un poco de:

    Rules are for Fools, Patterns are for Cool Fools. Journal of Object-Oriented Programming, 10, October 1999.

  7. Jose Selesan Says:

    Excelente post!. Yo creo que el secreto radica en la refactorización, junto con los unit test. Los metodologistas ágiles (si existe esa definición) siempre hablan o bien de KISS o bien de YAGNI (You Aren’t Gonna Need It), es decir, hacer que ande lo que tiene que andar, sin inventar nada raro ni nada que el usuario no haya pedido, y cuando el software empieza a crecer, se lo va refactorizando, llevando el diseño inicial a un diseño más OO, siempre apoyándose en los unit test para asegurarse de no haber roto nada.

    Eso sí, para todo eso se necesitan programadores con experiencia y buena voluntad. Experiencia para detectar qué cambios conviene hacer (y hacerlos bien) y buena voluntad para hacerlos. Lamentablemente me ha tocado ver aplicaciones que empezaron con KISS porque eran chiquitas y luego se convirtieron en mounstros pero no sufrieron ningún tipo de rediseño, y todos sabemos que eso puede llevar al caos de mantenimiento.

Leave a Reply