Resumen de Clean Code - segunda parte

oct 06, 2009 by Alfredo Casado

Despues de unas pequeñas vacaciones en paris, que ya me hacia falta irme de vacaciones, vamos a seguir con la segunda parte del resumencillo al excelente Clean Code del tio bob. Como comente el libro se dividia en capitulos teoricos y tecnicos, voy a seguir con el resumen de los capitulos teoricos por donde lo deje en la anterior entrada.

Capitulo 5: Formatting

Este capitulo bastante más "ligerito" que otros da algunos buenos consejos sobre formateo de código. Se divide en dos partes: formateo vertical y formateo horizontal.

Me ha gustado sobre todo lo del formateo vertical, en general cuando se habla de formateo es más habitual encontrar recomendaciones sobre formateo horizontal, la eterna batalla de la indentación con espacios o tabuladores por ejemplo. El formateo vertical me parece más interesante, la recomendación que más me gusta es lo que llama "The newspapper methapor" que basicamente consiste en escribir las clases de arriba hacia abajo siempre intentado colocar los métodos llamados inmediatamente despues del llamador, de forma que la clase se pueda leer sin tener que andar constatemente con el scroll para arriba y para abajo. Esto es algo que personalmente siempre he intentado hacer, pero sin pararme mucho a pensarlo, simplemente por sentido común, y me ha echo gracia verlo escrito y bien argumentado.

El resto del capitulo son recomendaciones sencillas para tener un código bien formateado, como por ejemplo usar espacios para resaltar la precedencia de operadores en las operaciones aritmeticas. Otra que me toca directamente es cuando desaconseja organizar el código en columnas, por ejemplo cuando se hacen varias asignaciones seguidas digamos en un constructor, yo personalmente quizas por que vengo de C++ tengo la tendencia a hacer que los iguales estén en la misma columna para que el código quede "cuadrado", y la verdad es que además de no servir para mucho es un poco engorroso sobre todo con el autoformateo de algunos IDE's que se empeñan en no dejarme hacerlo tranquilamente jeje, en esto creo que si me ha convencido el tio bob y poco a poco lo voy dejando.

Capitulo 6: Objects And Data Structures

En este capitulo se plantea fundamentalmente la diferencia entre objetos y estructuras de datos. Objetos son los elementos que ocultan su implementación interna y ofrecen métodos frente a las estructuras de datos que por definición exponen sus interioridades y no ofrecen ningún método.

El este capitulo el tio bob critica una practica muy común en el desarrollo java, la de los famosos getter&setter, basicamente considera a los beans estructuras de datos disfrazadas de objetos que no aportan gran cosa, en resumidas cuentas si vas a tener una clase con N propiedades privadas y un getter y un setter para cada propiedad no lo llames clase, llamalo estructura de datos y pon todas las propiedades publicas. Me gusta especialmente una frase sobre este tema: "The quasi-encapsulation of beans seems to make some OO purists feel better but usually provides no other benefit". El problema en java quiza sea que esta tan extendido el concepto de "bean" que muchos frameworks te obligan a utilizarlos, normalmente para usar introspección sobre estos objetos y hacer su "magia", pero si no estas obligado por ningún framework... ¿realmente necesitas todos esos get&set?

Otra cosa que puede llamar la atención es cuando afirma que la creencia de que todo es un objeto no es más que un mito, y que un programador maduro debe saber distinguir cuando necesita un objeto y cuando necesita simplemente una estructura de datos. La recomendación general que se da es que cuando los datos no varien y se quieran añadir funciones que operen sobre estos datos un enfoque con estructuras de datos, procedural vamos, es más adecuado, mientras que cuando lo que se quiere es tener la flexibilidad de trabajar con distintos tipos (aka polimorfismo) el enfoque OO es superior.

Dificil de aceptar algunas cosas de este capitulo para un taliban de la OO como yo :P, aunque es cierto que cuando generas un monton de getters&setters mecanicamente, aunque sea con un IDE, te queda el regusto de que estas haciendo algo totalmente innecesario y que algo anda mal...

capitulo que me ha parecido muy interesante porque cuestiona cosas que parecen asumidas como "normales" en el mundo java y en el mundo OO, en ocasiones conviene replantearse algunas que uno hace casi mecanicamente y probar otros enfoques, este me lo tengo que releer un par de veces más a ver si me convence del todo que yo sigo viendo objetos por todas partes jeje, pero al menos me ha echo replantearme algunas cosillas, que en definitiva es lo que espero cuando me leo un libro de estilo.

Capitulo 7: Error Handling

Otro tema habitualmente "polemico", empieza recomendando el uso de excepciones sobre códigos de retorno de las funciones, esto puede parecer hasta evidente para cualquier programador OO con un poco de experiencia, pero se encuentra uno cada cosa por ahí... he trabajado bastante con código en C construido con uso constante de códigos de error y se me ocurren pocas formas mejores para hacer el código cuasi ilegible.

Otra recomendación, en la que ultimamente parece que hay cierto consenso, es no utilizar checked exceptions. En esto es bastante tajante y empieza de una forma que lo deja claro "the debate is over". Afirma que si se puede construir código robusto sin checked exceptions en otros lenguajes como C#, python, ruby etc,etc en java también se puede construir código robusto sin checked exceptions. El principal problema que plantea sobre las checked es la violación del principio "open-close" derivada de las dependencias que generan las excepciones checked, ya que no queda más remedio que capturarlas y relanzarlas o colocar un throws haciendo que el código de las capas más altas tenga que conocer excepciones de bajo nivel varias capas más abajo. Personalmente coincido al 100% con esto, nunca uso excepciones checked y API's como JDBC me parecen un ejemplo perfecto de porque no hay que usarlas, es un martirio para el usuario de estos API's.

Otra recomendación importante que realiza en este capitulo es la de evitar tanto devolver como pasar "null", en lugar de devolver null lanzar una excepción o bien devolver un objeto que cumpla el interfaz esperado pero no haga nada.

Capitulo 8: Boundaries

En este capitulo se refiere a como tratar con los limites del sistema, es decir, cuando nuestro código interactua con librerías de terceros. Hoy en día nadie se imagina hacer una aplicación sin usar alguna que otra librería: log4j, hibernate, quartz, lucene... cada uno seguro que podría hacer una lista cuasi interminable con todas las librerías que ha tenido que utilizar.

La recomendación inicial para usar código de terceros en encapsularlo en lugar de tratar con el directamente integrado por varias partes de nuestra aplicación. La idea es sencilla, una librería por lo general ofrece gran cantidad de funciones para uso general de las que nosotros normalmente sólo queremos un subconjunto, de modo que la idea es crear nuestro propio interfaz que sólo ofrezca la funcionalidad que necesitamos e implementarlo utilizando la librería en cuestión. Esto ofrece varias ventajas:

  • Nuestra aplicación sólo interactua con un interfaz que ofrece justo lo que nos hace falta, ni más ni menos. Esto simplifica nuestra aplicación que no tiene que tratar con los detalles de la librería en cuestión.
  • Llegado el caso podriamos cambiar esta librería por otra de funcionalidades similares sin mucho esfuerzo. Esto no siempre es tan facil logicamente, pero si la librería de terceros la utilizamos diseminandola por todas partes de nuestro código nos estamos casando con ella, ya sabeis "hasta que la muerte nos separe", y hombre ¿tampoco nos vamos a casar con cualquiera no?
  • Encapsulamos toda la funcionalidad que necesitamos de la librería en una clase (o varias claro, dependiendo de la complejidad del tema), lo que hace que sea mucho más facil hacer pruebas unitarias, que por ejemplo nos permitiran descargarnos una nueva versión de la librería y saber que no se ha roto nada de lo que nos hacia falta.

Nosotros esto lo hacemos habitualmente con librerias, cuando hablamos de frameworks tan habituales en java con spring, struts, jsf etc,etc este enfoque no funciona, estos frameworks condicionan fuertemente la arquitectura de tu aplicación, vamos que si quieres meterle mano a alguno de estos te tienes que casar primero, son menos liberales :P.

Otra recomendación que me gusta de este capitulo es la de aprender a usar liberías de terceros mediante test unitarios. Todos en alguna ocasión hemos creado un "proyecto de prueba" con nuestro metodo main para probar esta o aquella librería, luego cuando hemos evaluado la librería y aprendido como usarla este proyecto suele acabar en la basura o olvidado en algún recondito lugar. La propuesta es hacer esta evaluación mediante test unitarios, de forma que hacemos un test por cada funcionalidad de la librería que queramos incorporar a nuestro programa. Estos test luego se incorporán a la base de código y sirven como una fantastica documentación del uso de la librería y para comprobar que nuevas versiones siguen funcionando como se esperaba. Un enfoque que me gusta bastante.

Y con esto termino la segunda parte, creia que con dos entradas llegaría pero me va ha hacer falta una tercera para los temas sobre clases, unit testing o diseño emergente, que me enrollo cosa mala :P



Comentarios:

Excelente post estimado. Felicitaciones.

Enviado por Germán G. en octubre 07, 2009 a las 01:05 AM GMT+01:00 #

Me ha hecho mucha gracia leer esta entrada en el log, ya que el formateo es una de los grandes hincapies que le hago a mis compañeros para que el código sea mas reutilizable/mantenible. Esto me da fundamento para poder discutir con ellos.
Gracias!

Enviado por Fernando en octubre 07, 2009 a las 07:45 AM GMT+01:00 #

Hola Alfredo,
respecto al orden de las funciones suelo usar la estrategia de poner las funciones públicas primero, luego las protegidas y finalmente las privadas y dentro de cada grupo las ordeno por la importancia que tienen dentro de la clase.
Lo del poner los get/set es desde mi punto de vista imprescindible ya que permites que la clase evolucione al poder calcula/almacenar/etc esa propiedad sin cambiar el contrato de la clase.
Respecto a las excepciones la norma que yo tengo es que solo las uso cuando hay un ERROR del programador en el programa y no en situaciones "anormales" pero lícitas en el programa.Por ejemplo si un usuario pone un importe "negativo" no lanzo una excepción sino que forma parte normal de flujo de la aplicación.
....sigue en el siguiente comentario....

Enviado por logongas en octubre 07, 2009 a las 06:03 PM GMT+01:00 #

Por el resto estoy al 100% de acuerdo con lo que dices del libro, especialmente con el tema de las checked exception y de encapsular las librerías.
Aunque con eso último siempre he tenido cargo de conciencia pq parecía que estaba haciendo sobreingenieria y saltándome el principio "KISS" (Keep it simple, stupid). Así que verlo aquí me ayudará a quitarme la mala conciencia.
Saludos.

PD:No me ha dejado comentarios de mas de 1000 carácteres.Así que lo he puesto en 2.

Enviado por logongas en octubre 07, 2009 a las 06:04 PM GMT+01:00 #

Yo no estoy del todo de acuerdo en el tema de los get/set: desde luego yo llevo tiempo pensando si el emperador va desnudo cada vez que usando hibernate me veo obligado a crear get/set por que sí.
Pero creo que en ese capítulo olvida muchas ventajas de esa aproximación: clases inmutables, builders para clases con muchas propiedades (si, existen!), entidades que además representan conceptos reales de dominio (y por tanto deben proporcionar funciones del dominio)...etc.
Es fácil meterse con el POJO get/set, pero muchos obvian diseños más complejos.
Salu2

Enviado por Ibon Urrutia en octubre 08, 2009 a las 06:53 PM GMT+01:00 #

logongas, sólo una recomendación: desde que uso la IllegalArgumentException, mi vida ha cambiado a mejor ;-)
salu2

Enviado por Ibon Urrutia en octubre 08, 2009 a las 06:57 PM GMT+01:00 #

Gracias por los comentarios!

Sobre el tema de los get&set, si lo único que hacen es directamente exponer propiedades de la clase, sin más, ciegamente, entonces es cuando no hay mucha diferencia entre eso y ponerlo todo public. Otra cosa es que decidas añadir a esas clases comportamiento y no sean simples estructuras de datos, lo dificil es elegir cuando es más adecuado una enfoque o el otro.

Un ejemplo, si leo un XML con JAXB este genera un conunto de clases tipo bean, muy bonito, pero en realidad el XML es una estructura de datos y me bastaria con clases con métodos públicos, además en este caso no tiene sentido añadir logica a estas clases que se va a autogenerar cada vez que cambie el xml. En casos como estos es donde no veo el valor de los get&set.

Enviado por Alfredo Casado en octubre 08, 2009 a las 08:57 PM GMT+01:00 #

"si leo un XML con JAXB"
Es que si ya apesta parsear un xml con Java a pelo, parsearlo con JAXB apesta dos veces ;-)
Salu2

Enviado por Ibon Urrutia en octubre 08, 2009 a las 09:21 PM GMT+01:00 #

Hola Ibon, el IllegalArgumentException lo uso cuando alguien ha programado algo mal y en la llamada al método ha pasado algún argumento mal. Se debe saltar la excepción pq hay un ERROR en el programa debido a un ERROR es un programador.
Pero si un usuario escribe mal un precio, ¿pq eso es una excepción? ¿Debemos tratar como excepción las equivocaciones del usuario?

Saludos.

Enviado por logongas en octubre 09, 2009 a las 08:08 AM GMT+01:00 #

Respecto al get/set no es que lo defienda "a muerte", simplemente lo veo un mal necesario y que tiene más ventajas que desventajas.A cambio del esfuerzo inicial de ponerlos, en un futuro permite modificar mas fácilmente la clase sin modificar el interfaz. Y eso para mi es fundamental.
Personalmente la solución que más me gustaba era la de VB 6.0 que podías tener una propiedad pública y en cualquier momento la podías encapsular dentro de un método pero se seguía viendo como propiedad. De esa forma se tenía la libertad de encapsular o no pero se mantenía el interfaz que era el de una propiedad.

Enviado por logongas en octubre 09, 2009 a las 08:14 AM GMT+01:00 #

logongas:
En el caso de que sean datos introducidos por los usuarios debería haber una validación previa de esos datos (otro método que validara esos datos). Si mi método espera precios se le deberían pasar precios. Ese método debe hacer una sóla cosa (otro de los consejos de clean code); si con los precios negativos ese método devuelve un valor "especial" está haciendo dos cosas: su objetivo y validar los datos del usuario.
Lanzar la IllegalArgumentExcepcion es simplemente que mi API es más sólida (ya que no sé si los clientes de esa API se habrán preocupado de hacer esa validación). Si quieres, desde el punto de vista de diseño por contrato, es comprobar las precondiciones.
Mi opinión es que una API que trabaja con precios que "funciona" con precios negativos es, por lo menos, desconcertante y a la larga mótivo de bugs y equívocos.
Salu2

Enviado por Ibon Urrutia en octubre 09, 2009 a las 08:25 PM GMT+01:00 #

Sobre el illegalArgument. En primer lugar la UI de usuario nunca debería permitir hacer algo mal al usuario, no le dejes poner valores negativos, no le dejes poner una edad menor de 0 o mayor de 300, esto a nivel de controles en el UI.

Si hablamos de un API, el API si debería devolver un IAE si recibe un valor fuera de rango, no queda más remedio. La cosa es que no me gusta nada llenar todos mis metodos con comprobaciones iniciales. Eiffel por ejemplo integraba la Programación por contrato en el lenguaje, en java tienes lo assert pero se quedan cortos, había algún intento de framework de Programación por contrato usando AOP, pero de momento no he encontrado nada que me convenza para tratar este tema de una forma que me guste, de momento comprobación manual y IAE al canto, feo, pero efectivo :P

Enviado por Alfredo Casado en octubre 09, 2009 a las 08:46 PM GMT+01:00 #

Hola Ibon,
creo que estás diciendo lo mismo que yo, así que supongo que no me he explicado bien.
Al haber el método previo de validación (el cual también uso), la excepción de IllegalArgumentExcepcion solo se generará debido a un error de programación. Que es justo lo que he querido indicar: Que las excepciones solo las uso para errores de programación.

Saludos.

Enviado por logongas en octubre 09, 2009 a las 11:36 PM GMT+01:00 #

Hola Alfredo,
ya he explicado a Ibon que la validación se hace antes y la IAE solo se generará debido a un error de programación.
Lo que no estoy de acuerdo es que dichas validaciones las tenga que hacer el UI o los controles. Esas son reglas de negocio como cualquier otra y por lo tanto deberían estar en un método "validate" en la propia clase de negocio.
Otra cosa es por ejemplo en la Web que por mejorar el rendimiento de la aplicación la quieras hacer en la página mediante JavaScript pero aun así la tendrás que repetir en el "validate" de la clase de negocio.

Saludos.

Enviado por logongas en octubre 09, 2009 a las 11:42 PM GMT+01:00 #

He estado viendo hace poco unos videos de escuela de Groovy, en donde comentan que groovy da la facilidad de trabajar con convención por sobre configuración para los accesores de atributos de las clases.

Quizás por ahí iría la solución que todos estamos buscando. ¿Has programador con Groovy Alfredo? ¿Logongas? ¿Ibon estimado?

Enviado por Germán G. en octubre 13, 2009 a las 12:49 AM GMT+01:00 #

Germán:
Lo mismo que groovy, lo hace Scala (y me parece que Jython y JRuby).
La verdad a mi me da igual que Java no lo haga, no hay IDE actual que no lo haga por mi. Se me ocurren otras muchas "urgencias" antes que esa.
Salu2

Enviado por Ibon Urrutia en octubre 13, 2009 a las 11:54 AM GMT+01:00 #

En efecto hay muy buenas recomendaciones, como las pruebas unitarias a las librerías, o la interfaz fachada a las librerías de terceros......pero en lo que si creo que se descalabró Robert Martín fue en el manejo de errores; los checked exception permiten flexibilidad en el lenguaje, delegando los errores al contexto y dándoles riqueza a las aplicaciones. Que las maneje el mismo método no significa que él las arregle, para lo cual es mucho más utiles relegarlas y dejarlas a quien las pueda entender y tratar de acuerdo al problema. Por otro lado, no me voy a poner a devolver un objeto que no hace nada siendo que hacerlo implica más procesamiento, uso de memoria......en fin, traer hijos al mundo concebidos para hacer nada :$. Prefiero seguir retornando y enviando el elegante y diciente null.

De cualquier manera (y obviando el capitulo 7), el libro ha ido muy bien....ha clarificado algunas cosas en cuanto a mi programación que dejaba pasar así no mas.

Muy buen post.

Enviado por Christian en diciembre 29, 2009 a las 08:38 PM GMT+01:00 #

Enviar un comentario:
  • Sintaxis HTML: Deshabilitado