jueves, 30 de marzo de 2017

Sobre estimar y por qué hacerlo en horas es una mala idea.

La respuesta rápida, sin entrar en matices, es muy simple: Nos conduce a tomar decisiones basadas en una mentira.

Alguien siempre dirá que necesitamos saber cómo planificar cada acción necesaria para llevar a cabo el proyecto en un plazo establecido; hay que marcar unos tiempos para valorar costes, y hay que poder hacer seguimiento de algún modo de los avances realizados. Sí, cierto, pero como tomemos esto de forma dogmática, queriendo tenerlo todo claro desde el principio, al final veremos que la realidad no coincidía con ese plan tan perfecto y realmente creíamos saber pero no sabíamos nada.

Aplicar agilidad responde a esa preocupación. Pero he visto un video resumen de SCRUM, que dura 10 minutos, que habla de estimar en horas. Quien haya leído el libro de Jeff Sutherland "Scrum: El nuevo y revolucionario modelo organizativo que cambiará tu vida", creador de SCRUM, se sorprenderá de que en el libro Jeff es muy duro con la estimación en horas, pero en algunos sitios aún se habla de estimar en horas.

Empecemos asumiendo que hemos decidido estimar en horas, así que, vamos a coger el listado de requisitos y a meter horas de cosas que no tenemos ni por qué conocer. Apliquemos entonces esa famosa formula matemática para estimar en horas. Vaya, parece que nadie la conoce. Ya tenemos aquí el primer problema. No hay una forma matemática de estimar, toda estimación está siempre basada en la perspectiva del que estima, y todos estamos llenos de sesgos que no nos dejan decidir de forma clara y fehaciente.

"What You See Is All There Is".


En "Pensar rápido, pensar despacio", Daniel Kahneman, habla de un sesgo cognitivo al que se refiere con el acrónimo WYSIATI ("What You See Is All There Is"). Este sesgo nos ayuda a tomar rápidas decisiones, sin que nuestro sistema lógico y lento interfiera, creando un entorno coherente eliminando la incertidumbre. Nos ayuda a "saltar a las conclusiones sobre la base de una evidencia limitada" y es la esencia del pensamiento intuitivo que utilizamos a diario. De las misma forma que este sesgo nos ayuda en nuestra vida diaria también nos puede jugar malas pasadas. A la hora de pensar nuestro cerebro crea un marco coherente que nos genera seguridad, y aquí es dónde un exceso de confianza en WYSIATI nos puede hacer caer en el error de ignorar todo eso que no estamos viendo. Tener en cuenta posibilidades que desconocemos puede estropear nuestra perfecta historia donde todo está controlado, por lo que se ignoran. Así es como se toman muchas importantes decisiones que acaban en desastre.

Cuando nuestro conocimiento es escaso, y eso suele ocurrir siempre que se estima ya que aún no nos hemos metido en el fango, por decirlo vulgarmente, es más fácil crear una historia coherente en la que todo encaja. Se estima por debajo, muy por debajo, y cuando surgen los problemas que no conocíamos surge el desastre ya que hay que meterlo todo en un plan totalmente calculado dónde la novedad inesperada ya no encaja. WYSIATI nos la ha jugado. 

Por tanto, entendiendo qué es WYSIATI, a la hora de estimar ignoraremos todo aquello que no vemos creando una estimación basada en escasa información.

Vamos a pesar cuan complejas son las cosas.


Asignando un peso en complejidad para estimar, tirando de la secuencia de Fibonacci para evitar números cercanos, es más fácil tener en cuenta esa incertidumbre que si usásemos horas. Decir que una cosa tiene una complejidad de 5 y la siguiente de 8 siempre es más fácil que, incluso con horas, decir que tardaremos 5 horas u 8 horas. Un peso no implica tiempo per sé, sólo complejidad, pero una hora es unidad de tiempo por definición.

Entonces, para estimar en horas, debemos al menos saber qué estamos estimando. Cuanto mejor lo conozcamos mejor será esa estimación. Pero es imposible conocer todos los detalles, sobre todo si es algo en lo que no hemos trabajado, y muchos detalles de lo que sí hemos trabajado somos incapaces de contemplarlos mentalmente hasta que los tenemos delante una vez hemos retornado a ello. Cuando comenzamos a refrescar la memoria vemos todo más claro. De ahí que surjan herramientas como el cono de incertidumbre que nos viene a decir que cuando estimamos algo hay un margen de error que al principio es muy grande y se va reduciendo, junto a la incertidumbre, según avanzamos en el proyecto acercándonos al final. El problema de esto es que, aún siendo conscientes de dicha incertidumbre, estipular horas desde el comienzo hace que sea muy tentador pensar que estamos hablando de un plazo que se cumplirá, sobre todo si es alguien no técnico quien lo valora. Un peso en complejidad, que puede modificarse con el descenso de la incertidumbre, siempre es más útil y fácil de manejar.

Y si seguimos pensando en horas tendremos muchas preguntas que hacernos.

¿De qué sirve una estimación en horas si el que estima no ha trabajado en lo estimado? De nada ya que desconoce totalmente cómo está hecho y el porqué de todos los detalles.

¿De qué sirve una estimación en horas si el que ha estimado sabe el cómo y el porqué pero no va a trabajar en ello? Esa estimación, basada en conocimientos personales, queda en nada porque quien ejecutará partirá desde otro punto de salida y una perspectiva de la situación totalmente distinta.

Entonces, ¿quien debe estimar cada nuevo requisito?

Estas preguntas son básicas para empezar a ver por qué usar horas no es una buena idea, sobre todo si la estimación la hace una única persona (algo habitual en proyectos tradicionales). Y aquí la agilidad nos aclara que debe estimar todo el equipo que va a trabajar en ello. El equipo, al estimar, tendrá más facilidad manejando pesos de complejidad que horas.

Otros sesgos que nos harán entender la complejidad de estimar que nos muestra Daniel Kahneman en su libro son: Efecto ancla y la falacia de la planificación.

Efecto ancla.


Se produce cuando las personas consideran un valor particular para una cantidad desconocida antes de estimar esa misma cantidad. Al final, debido a este sesgo, las estimaciones siempre están cerca de ese número que se ha considerado previamente, de ahí la imagen de un ancla. El efecto ancla está muy estudiado en psicología experimental y es aceptado de forma general.

Un ejemplo lo tendríamos cuando, para un requisito determinado, nos dicen: "Queremos entregarlo en 20 días, ¿cuando estará listo?". Al final la estimación rondará los 20 días, siendo unos 15 si lo consideramos sencillo y siendo 25 si lo consideramos difícil. Si nos hubieran dicho 30 días habríamos estimado en torno a los 30 días. Nos vemos condicionados por un valor que no debería haberse considerado para hacer la estimación.

Otro ejemplo de efecto ancla lo podemos tener en el tiempo que sabemos que tenemos y que nos va quedando según vamos estimando distintos requisitos. Si tenemos 1 mes para 5 requisitos, y ya hemos estimado 4, conocemos el tiempo que falta para llegar a un mes, por lo que el quinto requisito estará anclado a ese valor de tiempo pendiente de estimar. Al final ajustaremos el tiempo estimado de trabajo al tiempo disponible dentro del plazo determinado. Si el último requisito lo hubiéramos estimado primero el ancla habría sido más flojo, por lo que la estimación estaría menos condicionada al tener aún todo el tiempo disponible, pero aún así el mes de plazo seguiría ejerciendo de ancla ya que sabemos que tenemos otras cosas pendientes de estimar y no debemos pasarnos.

La falacia de la planificación.


Cuando se aborda un proyecto a largo o medio plazo se da una estimación en base al conocimiento interno que se tiene del proyecto en ese momento (WYSIATI  ataca de nuevo). Es lo que Kahneman llama la visión desde dentro, y en ella estaríamos atrapados ignorando los plazos confirmados por otros equipos en otros proyectos similares. Es la tasa base de la que deberíamos partir, pero al final siempre es ignorada y no se tiene en cuenta; pensamos que nosotros lo haremos mejor o no tendremos tantos problemas.

Estas previsiones iniciales, si no se tiene en cuenta la tasa base de la visión desde fuera, encierran una falacia de planificación: Las estimaciones están siempre más cerca del mejor de los casos que de una estimación realista.

Esa estimación inicial se lleva a cabo con el conocimiento de partida cuando todo es más sencillo y aún no han surgido problemas internos y externos.

Esto, en un proyecto estimado desde el primer momento, o incluso a 3 meses vistos para una nueva release, siempre acaba mal. ¿No estarían mejor iteraciones de trabajo cortas en las que estimamos una y otra vez con el conocimiento adquirido en cada iteración? Con agilidad volvemos a eliminar incertidumbre y complejidad.

¿Y cómo realizamos la estimación sea en horas o pesos?


Si nos vamos al libro The Clean Coder (libro imprescindible, por cierto) de Robert C. Martin vemos que hay un capitulo dedicado a estimar en el cual habla sobre:

- La ley de los grandes números. Cuanto mayor sea lo que hay que estimar peor se estimará.
- La interpretación de qué es una estimación es distinta para la gente de negocio que para los desarrolladores. Para la gente de negocio es una fecha a cumplir. Para los desarrolladores es una idea aproximada de lo que se va a tardar.
- Siempre hay que dar en la estimación un margen de error. Aquí Martin mete una formula en la que incluye la estimación que creemos es la más optimista, la que creemos más adecuada, y la que creemos es la más pesimista. Luego con la más optimista y más pesimista obtiene la desviación estandar para dar el margen de error.
- Advierte que un buen profesional nunca debe dar una estimación sin margen de error. Si un desarrollo va a tardar 4 días (si medimos tiempo y no peso) en realidad puede tardar 2, 4 o 6 si el margen de error son 2 días. Eso siempre que la creencia en las estimaciones aplicadas sea cierta; raramente lo es.
- Habla de utilizar la secuencia de Fibonnaci (horas, dias, puntos, ...).
- Habla del 'Planning Poker' y otras formas de estimación.

En horas todo es más complicado.

Algunos de estos puntos nos sonarán ya conocidos del libro de SCRUM (y del mundo ágil), ya que SCRUM no es más que un compendio de buenas prácticas con su propia esencia. Por ejemplo hacer planning poker viene de Extreme Programming pero también es utilizado en SCRUM (no es obligatorio).

La hora trabajo como unidad.


Y si aún seguimos pensando en horas podemos tratar de cuantificar qué es una hora. Aquí hablamos de la "Hora Trabajo" y no de 60 minutos.

Nos hacen falta ciertas preguntas.

¿Qué es una hora de trabajo cuando hablamos de trabajo no industrial? ¿Una hora equivale siempre a una hora? ¿Es lo mismo una hora de un lunes que de un viernes? ¿Es lo mismo una hora por la mañana que por la tarde? ¿Es lo mismo una hora en una tarea de alguien que haya trabajado en ello que de alguien que lo ve por primera vez? ¿Es lo mismo una hora de un novato que de alguien con mucha experiencia? ¿Es lo mismo una hora de trabajo tras una mala noche que tras una noche reparadora? ¿Es lo mismo una hora de trabajo el día que estamos preocupados por algo personal que el día que estamos felices? La respuesta a todas estas preguntas, y muchas similares que se pueden hacer, es NO: una hora no es comparable a otra hora. ¿Por qué entonces queremos saber cuantas horas se tardará si estamos estimando en una magnitud ("la hora trabajo") que dependiendo del momento tendrá un valor diferente? Un peso en complejidad vuelve a ser más apropiado.

Por eso no es posible afirmar que en X horas de trabajo estimado podemos aportar X horas de un desarrollador. Si algo necesita 30 horas no podemos aseverar que esas horas se pueden cubrir con 30 horas de alguien si una hora nunca es equivalente a otra. Asignar horas antes que elegir quien y cuando lo hará es muy típico, y de ahí que nunca sea buena idea.


Y cuando acabamos algo estimado en horas, ¿qué conclusión sacamos?


Y ya, si hemos estimado en horas, una vez finalizado el trabajo, tenemos las siguientes situaciones:

  1. Al final se necesitaba menos tiempo en horas del estimado. Aquí entra en juego la ley de Parkinson: "El trabajo se expande hasta llenar el tiempo disponible para que se termine". Así que si tenemos 4 días, y podríamos tardar 2, tardaremos 4 días en pos de la perfección y no de algo suficientemente bueno. La búsqueda de la perfección carece de sentido cuando el esfuerzo realizado apenas aporta valor respecto a algo suficiente bueno pero, si tenemos el tiempo, ¿por qué no aprovecharlo?
  2. El tiempo de trabajo coincide con el tiempo estimado. En la mayor parte de los casos esto será casualidad y sólo será cierto para pequeñas estimaciones de algo que tenemos muy fresco. Al ser casualidad podremos caer en el error de pensar que sí sabemos estimar en horas, cuando no sabemos, y decidir en base a todas esas estimaciones que no van a ser ciertas.
  3. Al final se necesita mucho más tiempo que el estimado, lo que nos lleva a retrasos no esperados o a acabar de forma precipitada con la merma de calidad correspondiente y la deuda técnica introducida en el desarrollo, lo que nos hará ir más despacio en el futuro. 

Todo esto son grados de complejidad con los que hay que tratar. Con agilidad, y sobre todo si decidimos estimar en peso de complejidad, podemos minimizar el problema asociado a toda esa incertidumbre que no nos permite saber cuando acabaremos.

Pero, una última pregunta pendiente.


Si el cliente me pide una estimación, y no estimo en horas, ¿cuando le digo que lo tendrá?


Como hemos medido la velocidad del equipo sabiendo cuanto peso de complejidad es capaz de resolver por sprint podemos saber cuantos sprints estimados necesitaremos para terminarlo, y un sprint sí es convertible en tiempo. Si necesitamos 3 sprints de 2 semanas cada uno tardaremos 6 semanas, pero cada 2 semanas podremos tener completada una pequeña parte con valor potencialmente entregable (si queremos entregarlo). 

Lo bueno de medir en peso es que, según el equipo mejora, podemos ir aumentando el peso en complejidad resuelto por sprint. Si en cada sprint el equipo completa 100 en peso, luego puede resolver 120, luego 140, y así estabilizarse en torno a una nueva velocidad, todo gracias al mejor conocimiento del proyecto y la mejoría de trabajo lograda. Por tanto, si volvemos al ejemplo anterior, en un nuevo requisito similar ya no necesitaremos 3 sprints, con 2 será suficiente, por lo que se puede entregar antes. Y no hemos necesitado re-estimar modificando el peso en complejidad, simplemente somos capaces de hacer mejor trabajo y ser más resolutivos. Eso no quiere decir que al principio de cada sprint no se puedan modificar pesos estimados gracias a nuevo conocimiento, aquí hablamos de medir la velocidad del equipo y que eso por sí solo nos permita crear valor de forma más rápida.

Si estimamos en horas en vez de en peso la cosa cambia. Si estimamos en 6 semanas, por mucho que mejore el equipo, 6 semanas son 6 semanas. Tendríamos que decir  que son 6 semanas estimadas pero que las vamos a meter en 4 semanas, y eso mentalmente es algo que no somos capaces de manejar. Entonces tendríamos que re-estimar muchas issues en tiempo, por lo que volvemos a los problemas iniciales y perdemos la capacidad de medir la velocidad del equipo. Podemos usar horas como si no fuesen horas, pero estaríamos usando una medida que cada participante en el proyecto interpretaría de una forma diferente.


domingo, 13 de noviembre de 2016

Factory Method. Parte III - Variante en PHP (u otros lenguajes)

Después de ver el caso más tradicional del patrón factory method, donde cada producto concreto tenía que tener una subclase del creador asociada, vamos a ver una variante para aplicar con lenguajes como PHP.

El principal problema del factory method reside en que, si tenemos muchos subproductos, vamos a tener una gran cantidad de subclases constructoras de esos productos, además de que la clase cliente debe heredar de la clase creadora; vamos a crear una jerarquía de clases como clase padre el creador que puede tener subclases muy similares entre sí.

En el anterior caso de la clase para crear una habitación, para diferenciarlo del caso que veremos a continuación, teníamos dos tipos de subproductos en cada subclase creadora: sillas y proyector. Lo más normal es que para aplicar el factory method tengamos sólo 1 tipo de producto, ya que si hay varios tipos de producto que deben trabajar siempre de forma conjunta el patrón evolucionará a un abstract factory pattern.

Así que, para comenzar, vamos a mostrar un ejemplo muy simple en el que el cliente usa un método creador para obtener distintos tipos de notificadores (email y sftp).
[code] $data = array( 1,2,3,4,5 ); $typeNotifications = array( Notifier::EMAIL, Notifier::SFTP, Notifier::EMAIL ); foreach ($typeNotifications as $type) { $notifier = Notifier::getNotifier( $type, $data ); echo( $notifier->notify() . PHP_EOL ); } [/code]
El ejemplo es muy simple, pero para imaginar un caso más real las variable $data y $typeNotifications podrían ser objetos con toda la información, o registros de una base de datos, o lectura de un fichero, ..., pero vamos a simplificarlo para ver con más claridad qué ocurre:

Detalles que llaman la atención:
  • El método creador es un método estático de la clase Notifier, es decir, no hay clase creadora.
  • Al método creador se le pasan parámetros.
  • En el ejemplo vemos que hay 3 tipos de notificaciones: email, sftp y otra vez email.

Tras ejecutar ese código tenemos el siguiente resultado:
[code] Notifyied by email next data: 1,2,3,4,5 Notifyied by sftp next data: 1,2,3,4,5 Notifyied by email next data: 1,2,3,4,5 [Finished in 0.1s] [/code]
Se observa que hemos obtenido en total dos notificadores tipo email y uno tipo sftp, tal como hemos indicado en la variable $typeNotifications. El polimorfismo entra en acción.

Entonces, ¿Dónde está la clase creadora concreta que nos devuelve el producto concreto que tiene asociado? No hay.

Veamos la clase Notifier:
[code] abstract class Notifier { const EMAIL = "email"; const SFTP = "sftp"; protected $data = array(); final private function __construct( array $data ) { $this->data = $data; } public static function getNotifier( string $type, array $data ) : Notifier { switch ( trim( $type ) ) { case self::EMAIL: return new EMailNotifier($data); break; case self::SFTP: return new SftpNotifier($data); break; default: throw new \Exception("Error: Notifier ".$type." is not available"); break; } } public abstract function notify() : string; } [/code]
Vamos a ver los puntos más importantes de esta clase:
  • Es una clase abstracta, por tanto no puede ser instaciada: no habrá objetos de la clase Notifier.
  • El método getNotifier() es estático, se puede llamar sin tener un objeto de la clase. Este es el método creador que, como se ve, devuelve una clase concreta de Notifier.
  • El constructor es privado. De esta forma una clase cliente sólo puede obtener un tipo Notifier a través del método creador.
  • El constructor es final. Con esto evitamos que las subclases puedan sobrescribir el constructor. Volvemos a reforzar que las subclases sólo se puedan obtener a través del método creador.

Los más destacado es ver que el método creador se ha movido a la clase abstracta del producto.

Con esto ya sólo necesitamos las subclases de Notifier.

Clase EmailNotifier:
[code] class EmailNotifier extends Notifier { /** * Notify */ public function notify() : string { return "Notifyied by email next data: ".implode(",", $this->data); } } [/code]
Clase SftpNotifier:
[code] class SftpNotifier extends Notifier { /** * Notify */ public function notify() : string { return "Notifyied by sftp next data: ".implode(",", $this->data); } } [/code]
Con esta variante del patrón factory method no es necesario crear una estructura jerárquica de creadores ni necesitamos que nuestra clase cliente herede de la clase creadora.

El factory method pattern, tal y como lo vemos en este ejemplo, gana mucho aplicándolo junto a principios SOLID como la inyección de dependencias.

El código de github aquí.

sábado, 5 de noviembre de 2016

Factory Method. Parte II - Ejemplo

Tras el post sobre la importancia de sacar la creación de clases concretas de nuestro sistema toca mostrar algunos ejemplos.

Voy a comenzar por el caso más tradicional, que es sobre el que más hincapié se hace en el libro Design Patterns: Elements of reusable object-oriented software. He estado pensando qué ejemplo poner, ya que en dicho libro vienen 2, uno muy genérico sobre una aplicación y la creación de tipos de documentos, y otro más amplio escrito en C++ sobre la generación de laberintos para un juego. He elegido crear un caso intermedio entre ambos, pero recomiendo leer los ejemplos del libro.

Caso: Tenemos un emulador de realidad virtual que en un momento dado crea una habitación de entretenimiento. El sistema debe ser capaz de elegir entre una habitación para ver videos y otra para escuchar audio. Para que podamos centrarnos en el patrón de diseño he creado clases muy simples.

Implementación de Factory Method: Esto es importante entenderlo para comprender el origen de la motivación del patrón. La propia aplicación, es decir, la clase que representa la habitación (EntertaimentRoom), es la que tiene los métodos creadores y los llama ella misma junto con otros métodos.

Lo primero de todo analicemos la clase EntertaimentRoom antes de aplicar Factory Method. Aquí todo el código.


Vemos las siguientes características:
  • Tiene un método begin() que hace que comience la actividad de la habitación.
  • Hay un método llamado begin_old, con un único condicional, que hace más de una cosa, por lo que se ha refactorizado en métodos individuales.
  • Por desgracia cada método individual contiene el condicional, es un bad smell de libro.
  • La clase está llena de llamadas a constructores de clases concretas, lo que hace que el código central esté acoplado a la creación de subclases.
  • El constructor recibe el tipo de habitación que se debe crear.

En la clase Main vemos cómo se instancia y cómo se usa un objeto EntertaimentRoom. Vemos que añadir o eliminar una subclase requiere tocar mucho el código central, y eso que está muy simplificado.
[code] $room = new EntertaimentRoom(EntertaimentRoom::VIDEO); $room->begin(); [/code]
En este punto decidimos aplicar el patrón Factory Method.

Aquí todo el código.

La idea principal es que cada subclase de la clase creadora decida qué objetos debe utilizar. Como hemos dicho antes la clase EntertaimentRoom es la que genera las clases concretas, por lo tanto es la que llama a los métodos de creación; es la clase creadora.

La clase EntertaimentRoom tendrá subclases que sobrescriben los métodos creadores. EntertaimentRoom nunca sabrá qué clases concretas hay que crear, ese conocimiento estará en sus clases hijas.

Hacemos que EntertaimentRoom sea ahora una clase abstracta con las siguientes características:
  • Los métodos createPlayer(), getStartInstructions() y createChairs() son ahora abstractos. Se implementan en cada subclase.
  • El constructor ya no necesita recibir el tipo de la habitación, ya que no hay ningún condicional. Desaparece la variable de clase $type.
  • Creamos las clases AudioEntertaimentRoom y VideoEntertaimentRoom como subclases de la clase creadora.
  • En el fichero Main hacemos un cambio aprovechando las características de PhP. En un fichero json metemos el tipo de habitación, pero podría leerse de base de datos, generarse forma aleatoria o ser introducido por el usuario.

Este sería el nuevo fichero Main:
[code] $dataFile = file_get_contents(__DIR__."\config.json"); $roomConfig = json_decode($dataFile); $strClassRoom = "blog\\factory\\rooms\\".ucfirst(strtolower($roomConfig->type))."EntertaimentRoom"; $room = new $strClassRoom(); $room->prepareChairs(); $room->begin(); [/code]
Y este el json:
[code]{"type":"audio"}[/code]
Con ese JSON el fichero Main.php genera una clase AudioEntertaimentRoom que crea, gracias a los métodos de creación, objetos Player y Chair apropiados para ese tipo de habitación. Cambiando el json podríamos crear una habitación de tipo VideoEntertaimentRoom.

Ahora para añadir una nueva habitación sólo tendríamos que construir una nueva subclase de EntertaimentRoom y subclases Player y Chairs.

En versiones anteriores de PhP tenemos el problema de que no sabemos qué devuelve cada clase abstracta, por lo que habría que mirar qué puede devolver cada subclase, pero en PhP7 podemos tipar los retornos, incluso poner tipado estricto, y con ver las interfaces ya sabríamos qué debe devolver cada método. No haría falta ni tocar el código central ni mirar otras subclases.

Esta implementación tiene las siguientes características.

La clase cliente es la que tiene los métodos creadores, ella misma los usa, por lo tanto la clase cliente hereda de la clase creadora. Con la implementación realizada tendríamos una subclase de creador por cada tipo de producto.

Si la clase creadora sólo generase un subtipo de Producto podríamos no necesitar crear subclases de la clase creadora. La clase creadora sería concreta y podría incluir un método creador parametrizado que devolvería el tipo de producto.

El ejemplo realizado tiene otra variante: la clase EntertaimentRoom no es abstracta e implementa los métodos creadores. Las subclases del creador, de necesitarse, podrían sobrescribir parte del método creador de la clase padre; por ejemplo si hay dos habitaciones con el mismo objeto Player pero distinto objeto Chairs. Incluso aplicando propiedades del lenguaje utilizado, y pasando un parámetro, podríamos evitar tener una clase creadora (se verá en otro post).

Y para finalizar, ¿Qué ocurre si EntertaimentRoom ya hereda de Room? ¿Y si hay muchos objetos productos? ¿Y si para aplicar Factory Method tenemos que forzar que el cliente herede de la clase creadora? No es recomendable ya que estamos condicionando el futuro. Todas estas preguntas nos llevan a evolucionar de Factory Method a Abstract Factory Pattern, el cual puede utilizar Factory Methods como base, pero esa es otra historia que dejaremos para otro día.

Código sin Factory Method aquí.
Código con Factory Method aquí.
Código con variante de Factory Method aquí.

jueves, 20 de octubre de 2016

Factory Method. Parte I - Introducción

Una norma básica para evitar acoplamiento entre clases concretas y el cliente que las usa es trabajar para la interface y no para la implementación. De eso se trata: no queremos clases concretas en nuestro código.

Tenemos nuestro código, con la lógica de negocio, que utiliza distintas clases que heredan de una interface común, pero, ¿cómo hacemos para no utilizarlas directamente? Esa es la gran incógnita a resolver.

¿Por qué no queremos clases concretas en nuestro código?


Imaginemos los siguientes casos:

  • Tenemos código que queremos reutilizar en distintos proyectos ya que la parte central hace algo concreto, pero en cada proyecto, por lo que sea, ese algo concreto se hace de distintas formas. 
  •  Queremos que nuestro código central no tenga que ocuparse de cosas que no son su cometido. Dicho de otra forma, tenemos código central que para lograr su fin puede utilizar distintos caminos que no tenemos porqué conocer, queremos que el sistema sea independiente de sus productos.

En ambos casos aprovechamos clases concretas de un mismo tipo. Utilizamos polimorfismo. En estos casos la clase principal no puede saber qué productos debe crear.

Esas clases concretas se pueden seleccionar en un largo condicional dentro del código central, y eso es precisamente lo que queremos evitar. Y además, para el condicional necesitaríamos un parámetro en el código central que no haría falta para nada más.

¿Qué pasa si necesitamos crear una nueva clase concreta? ¿O nos vamos a otro proyecto con clases concretas diferentes?

Habría que modificar el código central, la lógica de negocio. Cualquier programador que meta una clase concreta nueva debería tocar esa parte, y eso no es recomendable. ¿Por qué no sacamos esa creación de clases concretas fuera de la lógica central (fuera del framework)?

Por eso no queremos acoplar la lógica de negocio a la creación de clases concretas.

Queremos que el código central se centre sólo en qué hace, trabajando contra una interface, sin conocer así a las clases concretas, y que la creación de estas esté desacoplada; si añadimos una nueva clase concreta no tendremos que tocar la parte central del código.

¿Cómo creamos entonces esas clases concretas?


Aquí entran en juego distintos patrones de diseño, y en ese caso concreto el Factory Method.

La definición del propósito de este patrón en el libro Design Patterns: Elements of reusable object-oriented software es la siguiente “Define una interfaz para crear un objeto, pero deja que sean las subclases quienes decidan qué clase instanciar. Permite que una clase delegue en sus subclases la creación de objetos”.

Aquí acaba está introducción con la necesidad de tener clases que nos ayuden a crear objetos. En sucesivos posts pondré ejemplos concretos del factory method pattern.

miércoles, 12 de octubre de 2016

Carga automática de clases. Parte II.

Tras haber visto un caso muy básico de cómo crear un atuloader en php llegamos al caso más habitual en el que tenemos una estructura de carpetas. Si organizamos el caso básico donde todo estaba en el mismo directorio podríamos tener la siguiente estructura.
[code] Directorio: Proyecto:
    Directorio: Entities
        Account.php
        Output.php
    autoload.php
    test_main_autoload.php [/code]

Parte II. Autoload: Implementación de PSR-4.


El primer pensamiento sería añadir a la función spl_auload_register del caso básico la nueva ruta para los requires, pero volveríamos al problema inicial si tenemos x directorios. Crear código que fuese comprobando en cada subdirectorio antes de pasar al siguiente si existe el fichero y si es así requerirlo no es algo muy eficiente; esto lo descartamos directamente.

Lo primero que debemos hacer, para encarar una solución óptima, es hacer que cada fichero sepa en qué directorio está. ¿Cómo? Utilizando namespaces.

¿Qué son los namespaces? .Digamos que es la forma de paquetizar ficheros en un mismo ámbito, es decir, es un espacio que creamos bajo un mismo nombre para agrupar clases, variables, funciones,… Es similar a los packages de java (sólo similar). Cada namespace puede tener el nombre que queramos poner.

En este caso pondremos a cada namespace un nombre base e iremos añadiendo la ruta dónde se encuentra cada directorio simulando una jerarquía de namespaces.

Veamos el fichero principal que está en el namespace base.

[code] namespace blog\autoload; use blog\autoload\Entities AS ent; require_once "autoload.php"; try { $account = new ent\Account(5); $output = ent\Output::getOutput(ent\Output::CONSOLE); $account->addAmount(2000); $account->payCommision(); $account->addAmount(50); $account->recoverAmount(20); $output->print($account->getTotal()); } catch (Exception $e) { echo($e->getMessage().PHP_EOL); } [/code]
Vemos que lo primero que se hace, en la primera línea, es asignar el fichero al namespace base blog\autoload (podemos poner el nombre que queramos) y a continuación utilizamos el namespace blog\autoload\Entities (base + primer directorio) y le damos un alias para acortarlo.

Ahora vamos a ver las clases Account y Output pertenecientes al namespace blog\autoload\Entities.

Clase Account:
[code] namespace blog\autoload\Entities; class Account { private $commision; private $amount; public function __Construct(int $percentage) { $this->commision = $percentage; } public function addAmount(int $amount) { assert($amount >= 0); $this->amount += $amount; } public function payCommision() { $this->amount = ( ( 100 - $this->commision ) * $this->getTotal() ) / 100 ; } public function recoverAmount(int $amount) { assert($amount >= 0); $this->amount -= $amount; } public function getTotal() : int { return $this->amount; } } [/code]
Clase Output:
[code] namespace blog\autoload\Entities; class Output { const CONSOLE = 1; const FILE = 2; private function __construct() { // Configure output } public static function getOutput(int $type) : Output { // Aquí deberíamos tener un factory method para devolver el objeto output adecuado y Outpt debería ser una clase abstracta. // Queda pendiente para otro post donde se muestre cómo generar objetos. if($type === 1) return new Output(); else throw new Exception("Wrong ouput selected."); // Si no existe un output adecuado deberíamos devolver un output base en vez de lanzar una excepción. // No tener un output adecuado no debería para la ejecución. } public function print($val) { echo("Total amount is: ".$val.PHP_EOL); } } [/code]
Vemos que ambos ficheros pertenecen al namespace blog\autoload\Entities, y es en el directorio Entities dónde se encuentran.

Hay que tener en cuenta un detalle antes de continuar; aunque el namespace contenga una ruta realmente no estamos asignando una ruta, estamos asignando un nombre que casualmente coincide con la ruta. Si hacemos que un fichero use el namespace vendor\dir1 no estaremos usando a su vez vendor\dir1\dir2 aunque el directorio dir2 esté dentro de dir1, son nombres de namespaces distintos, repito, no rutas, un namespace no contiene a otro.

Antes de ver el autoload analicemos los detalles y el patrón a seguir.

  • Podemos decir que el fichero ejecutable está en el namespace base, por lo que puede tener cualquier nombre, en este caso hemos decidido que sea blog\autoload\.
  • Cada clase debe tener el mismo nombre que el fichero que lo contiene (una clase por fichero). 
  • Cada fichero deberá pertenecer al namespace compuesto por el nombre del namespace base más las barras correspondientes y los directorios en los que está cada fichero. En este caso al estar en el directorio Entities que cuelga del directorio base su namespace es blog\autoload\Entities.
  • El nombre de las clases tiene la estructura namespace\subnamespace\..\Class. Por ejemplo la clase que llega al autoload realmente es la clase blog\autoload\Entities\Account. Es decir, su namespace más el nombre de la clase.
  • Se observa que el nombre de la clase es namespace_principal\directorio1\..\Clase, por lo que si eliminamos el namespace base, y añadimos un .php al final, tenemos exactamente dónde está el fichero guardado respecto al directorio base. Ejemplo: __DIR__.\Entities\Account.php.

Siguiendo estos puntos ya tenemos un patrón: el nombre del namespace indica dónde está cada fichero respecto a un namespace base.

Pues ya es hora de ponernos a construir un fichero autoload. ¿O no? ¿Para qué crear el autoload si ya alguien lo ha creado por nosotros?

La gente de PHP-FIG ya ha creado un autoloader que podemos utilizar. Lo podéis encontrar en su recomendación estándar 4 (PSR-4 o PHP Standard Recommendation 4).

Aquí lo hemos adaptado a nuestro ejemplo modificando las variables $prefix y $base_dir y añadiendo un echo para imprimir la clase requerida.
[code] // Implementation of PSR-4 spl_autoload_register(function ($class) { echo("Requiriendo fichero: ".$class.".php".PHP_EOL); // project-specific namespace prefix $prefix = 'blog\\autoload\\'; // base directory for the namespace prefix $base_dir = __DIR__.'/'; // does the class use the namespace prefix? $len = strlen($prefix); if (strncmp($prefix, $class, $len) !== 0) { // no, move to the next registered autoloader return; } // get the relative class name $relative_class = substr($class, $len); // replace the namespace prefix with the base directory, replace namespace // separators with directory separators in the relative class name, append // with .php $file = $base_dir . str_replace('\\', '/', $relative_class) . '.php'; // if the file exists, require it if (file_exists($file)) { require_once $file; } }); [/code]
Con esto ya tenemos nuestro fichero autoload.php que hemos requerido anteriormente en nuestro fichero principal.

Lo ejecutamos y obtenemos el siguiente resultado.
[code] Requiriendo fichero: blog\autoload\Entities\Account.php Requiriendo fichero: blog\autoload\Entities\Output.php Total amount is: 1930 [Finished in 0.4s] [/code]
La ejecución ha sido satisfactoria.

De esta forma, requiriendo el autoload y usando un namespace (blog\autoload\Entities), podemos autocargar todas las clases que contenga este sin tener que hacer un require por cada fichero.

Código en GitHub

domingo, 9 de octubre de 2016

Carga automática de clases. Parte I.

Es frecuente encontrar, al comienzo de ficheros php, largas listas de requires con las clases que se van a utilizar. Y en esos otros ficheros que requerimos encontramos a su vez otras listas de requerimientos. Es molesto tener que escribirlos todos, o borrarlos si movemos instanciaciones, pero tiene solución creando un cargador de clases.

Parte I. Autoload simple.



Un sencillo ejemplo en el cual todos los ficheros php están en el mismo directorio.

Tenemos el siguiente fichero php:
[code] require_once "Account.php"; require_once "Output.php"; try { $account = new Account(5); $output = Output::getOutput(output::CONSOLE); $account->addAmount(2000); $account->payCommision(); $account->addAmount(50); $account->recoverAmount(20); $output->print($account->getTotal()); } catch (Exception $e) { echo($e->getMessage().PHP_EOL); } [/code]
Vemos que utiliza 2 clases: Account y Output. Este caso es muy simple, pero si utilizáramos 50 clases deberíamos hacer un listado con 50 requires. Una locura no apta para dedos sensibles a escribir una y otra vez cosas que no aportan nada.

¿Qué no aporta nada? ¿Y qué hacemos entonces? Que si lo quito porque no quiero escribirlo me salta el siguiente error:
[code] ‘Fatal error: Class 'Account' not found’ [/code]
Pues aprovechemos la siguiente función de php: spl_autoload_register.

Vamos a crear un fichero básico (muy básico) llamado autoload_basic.php.
[code] spl_autoload_register(function ($class) { echo("Requiriendo fichero: ".$class.".php".PHP_EOL); require_once $class.".php"; }); [/code]
Si no entiendes muy bien qué ocurre, y por qué hay una función cómo parámetro, tiene que ver con las closures. ¿El qué? Es tema para otro post, pero quedémonos con el siguiente resumen: el parámetro que acepta la función spl_autoload_register es de tipo callable, es decir, lo que se le pasa es una llamada de retorno. La función que se pasa como parámetro es una closure o función anónima, y su tipo es Closure. Las funciones anónimas, es decir, los tipos Closure también se pueden pasar como un parámetro callable.

La función que se registra en spl_autoload_register es el autoload que queremos que se ejecute cada vez que se quiere instanciar una clase.

Cambiemos la función principal:
[code hl="1"] require_once "autoload_basic.php"; try { $account = new Account(5); $output = Output::getOutput(output::CONSOLE); $account->addAmount(2000); $account->payCommision(); $account->addAmount(50); $account->recoverAmount(20); $output->print($account->getTotal()); } catch (Exception $e) { echo($e->getMessage().PHP_EOL); } [/code]
Ya está, todo listo para probar.

Fijemos antes que los requires a cada fichero han desaparecido y ahora estamos haciendo un único require de autoload_basic.php.

Ejecutemos:
[code] Requiriendo fichero: Account.php Requiriendo fichero: Output.php Total amount is: 1930 [Finished in 0.1s] [/code]
Con este resultado sacamos la siguiente conclusión:
  1. El ejecutable trata de instanciar una clase.
  2. El fichero que contiene la clase no ha sido requerido, por lo que se ejecuta la función autoad que se ha registrado a través de spl_autoload_register.
  3. La variable $class es el nombre de la clase que se trata de instanciar.
  4. Dentro de la función autoload se hace el require de la clase.
  5. Ya no hay que hacer x requires en el fichero principal.

Todo perfecto y en orden, salvo que todo no puede ser siempre tan simple.

NOTA: El fichero autoload_basic.php, para este caso, podría ser de la siguiente forma:
[code] spl_autoload_register(); [/code]
Si la función spl_autoload_register no recibe ningún parámetro usa directamente como variable callable por defecto la función spl_autoload que a su vez tiene como parámetro el nombre de la clase que se instancia. He preferido añadir la closure para que tenga el mismo efecto pero que se vea qué hace.

Código en GitHub

domingo, 25 de septiembre de 2016

Contra los largos métodos

Estos últimos días he estado revisando código para el testeo (no hay tests unitarios) de una aplicación realizada en symfony. He estado tratando de entender qué hace para poder ver si funciona o no.

Bien, en un punto, probando diferentes tipos de alarmas que heredan de una clase madre, he visto que cada una de esas clases tiene un método run. Hasta ahí bien, pero me he encontrado dos cosas que no me han gustado.

  1. El polimorfismo no se usa en una estructura, ni pudiendo pasar distintos objetos cómo parámetros para ejecutar el método a posteriori. Cada método run de cada alarma se ejecuta de forma autónoma en condiciones diferentes nada más instanciar el objeto de cada alarma concreta. 
  2. El método run es el único método de la clase concreta y cada uno tiene entre 200 y 400 líneas.

Evidentemente entender a veces qué hacen esos métodos tan largos me está generando ciertos quebraderos de cabeza, y alguna vez me pierdo y tengo que volver atrás, cómo quien se duerme viendo una película y busca dónde se quedó antes de sucumbir.

Así que he aprovechado para resumir qué dice Martin Fowler en su libro “Refactoring: Improving the design of existing code”.

Recordemos que un método debe hacer una única cosa, y en 300 líneas de método me da que caben bastantes.

Basta decir que lo primero que dice, como antítesis del enunciado “Long Methods”, es que los métodos deben ser cortos. ¿Pero cómo? Ahí viene la parte interesante.

He decidido no meter aquí ejemplos de código para no hacerlo muy largo y que se pueda leer como un simple manual con recomendaciones a seguir. Ya vendrá el código concreto en otros posts.


Extraer métodos.



Lo más básico es que si hay código duplicado dentro del método, o que hace casi lo mismo en varias partes, hay que sacarlo como un método en sí mismo. Esto es así siempre. El código no se duplica.

Hay veces en los que hay un trozo de código que dan ganas de comentar para que se pueda entender, por lo que eso tiene toda la pinta de que se puede extraer en un método. Aquí hay que tener cuidado y no extraer métodos por extraerlos. Por ejemplo, si vas a extraer 2 o 3 líneas (o alguna más) quizás no merezca la pena, o quizás sí. Ahí entra el caso concreto y la valoración del contexto. ¿El método tiene entidad por sí mismo? ¿Tendría sentido sin el método del que se ha extraído?

Los métodos extraídos deben tener buenos nombres. El nombre debe contener la intención del método, no cómo hace lo que hace. Esto es importante. ¿Es difícil elegir el nombre con la intención? Quizás es que tiene más de una intención y no es un único método a extraer. Resumiendo, el nombre del método debe decir lo mismo que el comentario que se iba a añadir, pero sólo en un nombre.


Reemplazar variables temporales con una consulta.



Si al método extraído se le pasan muchos parámetros que no son más que variables temporales, creadas únicamente para contener resultados de ciertas expresiones, tenemos un problema. Muchos parámetros complican entender qué hace el método, y por otra parte estamos acoplando el método extraído a resultados calculados en el largo método que queremos acoplar. No aportamos comportamiento sólido a la clase con ese nuevo método.

Hay que eliminar esos parámetros. IMPORTANTE: Esto aplica a variables temporales que sólo se asignan una vez, ya que vienen de una expresión que ha realizado un cálculo, y dicha expresión no tiene efectos colaterales. Es decir, no modifica el estado del objeto. Un ejemplo típico es el cálculo a base de variables de clase para obtener un valor que se usa temporalmente. Ese cálculo, que no modifica ninguna de las variables de clase, se puede extraer en un método, aportando comportamiento a la clase.


Introducir un objeto con los parámetros.



Vale, tenemos parámetros que no son variables temporales, son variables que se usan a lo largo del método que incluso pueden cambiar durante la ejecución, y encima vemos que habitualmente se pasan de forma conjunta. ¿Cómo procedemos si no queremos pasar una gran cantidad de parámetros al método extraído? Siempre se puede pasar un array como parámetro, pero eso sería sólo en el caso de que tuviésemos pocos parámetros sin ningún tipo de comportamiento. ¿En esos casos merece la pena crear una nueva clase? Lo más probable es que no.

Pero en el caso en el que durante la ejecución del método las variables a pasar puedan cambiar, o sean partícipes de expresiones (duplicidad de código asegurada), lo ideal es extraer esas variables, y su comportamiento, que será más evidente al agruparlas, en una clase para pasar así un objeto como parámetro. Esto facilitará acortar bastante el método y encapsular variables y comportamiento en un único objeto, lo que a la larga favorecerá la comprensión y modificación del código.

Resumiendo, vamos a crear, a base de variables y el comportamiento que conllevan, un objeto que se pasará como parámetro al método extraído.


Pasar el objeto completo.



Bueno, este es muy sencillo, si pasamos cómo parámetros muchas llamadas a métodos de un objeto, o incluso muchas variables de clase del propio método, siempre podemos pasar el objeto completo, aunque este sea el propio objeto desde el que se pasan los parámetros si estamos pasando un montón de variables de clase recuperables con getters.


Extraer un método como un método de otro objeto.



Como ya hemos hecho anteriormente al introducir un objeto con los parámetros, volvemos a crear una nueva clase para reducir código de un método largo. Esta vez hay una diferencia. Se extrae un método y, a partir del nombre de ese método, se crea una clase que se instancia y usa. ¿En qué casos hacemos esto?

Aquí volvemos a encontrarnos variables temporales que se usan en el método original pero, debido a su complejidad, no pueden extraerse con una consulta. La idea a priori es simple (aunque en el fondo compleja); se crea una clase con variables de clase que representan esas variables temporales, se calculan en el constructor, y se crea un método con nombre tipo “compute”, “calculate” o similar. Teniendo ahora ese método aislado podemos aplicar los pasos anteriores para reducirlo, en la nueva clase, en métodos más pequeños.

En resumen, hemos creado un objeto a partir de un método que se instancia para ejecutar el método. Esto, si no se hace con cuidado, puede generar en clases erróneas que no es necesario crear y que realmente no representan una entidad. Otra forma de refacrorizar es eliminar clases que no lo son porque sólo hacen una cosa y no representan ninguna identidad. Este caso, por tanto, hay que hacerlo con mucho cuidado; un ejemplo sería el patrón “strategy pattern” en el cual se extrae comportamiento de una clase para usar una estrategia u otra tirando de composición en vez de herencia.


Descomponer un condicional.



Hemos encontrado una sentencia if-elseif-else muy compleja que no favorece la legibilidad y que, en algunos casos, puede incluso matar unas cuantas neuronas al tratar de seguir su lógica.

Un ejemplo sería un if tipo si es A y no es B y tampoco es C pero tiene que tener D, y todo ello lo podemos resumir en un es E. Creamos un método llamado E que maneja las comprobaciones múltiples, por lo que en el condicional sólo se llama a E dejándolo mucho más claro. Recordemos, el método tiene que tener un nombre que deje totalmente clara la intención del condicional compuesto y dejar el cómo en la implementación del método.