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.

lunes, 15 de agosto de 2016

Testeando métodos privados en PHP

La idea de este post es hacer testeo unitario de distintos métodos de una clase, aunque estos sean privados. Lo ideal sería utilizar un framework especializado, como puede ser PHPUnit, pero para simplificar voy a realizar testeo a “pelo”.

Cuando estamos testeando una clase siempre llega el momento en el que nos damos cuenta que no es posible testear un método privado de forma unitaria, pero queremos hacerlo; siempre lo estamos testeando indirectamente a través de un método público.

¿Cómo podemos testear de forma unitaria ese método privado?

La solución es utilizar reflexión (reflection).

Tenemos la siguiente clase llamada RobotCar:
[code] /** * Robot car moving along a road with several lanes. It can just go ahead and change rails left or right. * It is able to accelerate */ class RobotCar { private $x; private $y; private $acceleratorFactor; public function __construct() { $this->x = 0; // position in axe x $this->y = 0; // position in axe y $this->acceleratorFactor = 1; } public function changeToLeft ( array $viewAtLeft = array() ) { if($this->isSideFree($viewAtLeft)) $this->changeX(-1); } public function changeToRight ( array $viewAtRight = array() ) { if($this->isSideFree($viewAtRight)) $this->changeX(1); } private function changeX($mov) { $this->x += $mov; } private function isSideFree( array $view ) : bool { if(empty($view)) return true; return false; } public function goStraight() { $this->y += $this->acceleratorFactor; } public function getPosition() : string { return "X: ".$this->x." - Y: ".$this->y; } public function accelerate() { $this->acceleratorFactor += 1; } } [/code]
Esta clase representa un coche robot que, en un tablero de varios carriles (infinitos para simplificar), puede avanzar sólo hacía adelante (desplazamiento en eje y), con un factor de aceleración configurable, cambiando de carril si es necesario (desplazamiento en eje x).

Vemos que hay dos métodos, changeToLeft y changeToRight, que utilizan el mismo método privado isSideFree para verificar si es posible realizar el cambio de carril:
[code] private function isSideFree( array $view ) : bool { if(empty($view)) return true; return false; } [/code]
El método isSideFree recibe un array llamado $view que contiene todo objeto del mundo real que se ve antes de realizar el cambio y devuelve un booleano. ¿Cómo probarlo de forma unitaría?

Vamos a utilizar la clase de PHP ReflectionClass.

El primer paso es instanciar un objeto de dicha clase y extraer el método privado que vamos a utilizar.
[code] $reflected = new ReflectionClass("RobotCar"); $method = $reflected->getMethod("isSideFree"); [/code]
Ya hemos obtenido el método en la variable $method. El método es privado, por lo tanto hay que hacerlo accesible desde fuera:
[code] $method->setAccessible(true); [/code]
Ahora sólo queda invocar el método:
[code] $isFree = $method->invokeArgs(new RobotCar(), array(array("Arbol"))); [/code]
Vemos que los parámetros de invokeArgs son un objeto de la clase RobotCar y un array con los distintos parámetros del método a ejecutar. En este ejemplo pasamos un objeto recién instanciado, pero también en posible pasar un objeto instanciado anteriormente y modificado.

De esta forma, gracias a la técnica de reflexión, podemos testear métodos privados de forma unitaría como si se tratara de métodos públicos.

Probemos la clase con el siguiente código:
[code] $car = new RobotCar(); echo($car->getPosition().PHP_EOL); $car->goStraight(); echo($car->getPosition().PHP_EOL); // x = 0 $car->changeToLeft(); echo($car->getPosition().PHP_EOL); // x = -1 $car->goStraight(); $car->changeToLeft(array("Other Car")); echo($car->getPosition().PHP_EOL); // x = -1 $car->accelerate(); $car->goStraight(); $car->changeToRight(array()); // X = 0 echo($car->getPosition().PHP_EOL); // Test RobotCar::isSideFree. Is it possible? $reflected = new ReflectionClass("RobotCar"); $method = $reflected->getMethod("isSideFree"); $method->setAccessible(true); echo("\nTest private method RobotCar::isSideFree if there is a tree."); $isFree = $method->invokeArgs(new RobotCar(), array(array("Tree"))); assert(!$isFree,"There is a Tree, assertion must be false."); echo("\nTest private method RobotCar::isSideFree if there is nothing."); $isFree = $method->invoke(new RobotCar(), array()); assert($isFree, "There is nothing, assertion must be true."); echo("\nTest private method RobotCar::isSideFree if there is a dog on a big stone (test incorrect)."); $isFree = $method->invokeArgs(new RobotCar(), array(array("Big Stone", "Dog"))); assert($isFree,"There is a dog on a big stone, assertion must be false."); // The test is wrong inentionallity. [/code]
EL resultado devuelve:
[code] X: 0 - Y: 0 X: 0 - Y: 1 X: -1 - Y: 1 X: -1 - Y: 2 X: 0 - Y: 4 Test private method RobotCar::isSideFree if there is a tree. Test private method RobotCar::isSideFree if there is nothing. Test private method RobotCar::isSideFree if there is a dog on a big stone (test incorrect). Warning: assert(): There is a dog on a big stone, assertion must be false. failed in \src\test_reflection.php on line 96 [/code]
Vemos que los 2 primeros assertions no saltan ya que tanto el resultado del método a testear como el test son correctos. El tercer assert lanza un warning porque el resultado no es el esperado. Nota: Este tercer assert es incorrecto, lo correcto sería:
[code] assert( ! $isFree,"There is a dog on a big stone, assertion must be false."); [/code]
ya que se esperaría un false. He falseado el test para demostrar que este funciona correctamente y detecta el valor no esperado.

De esta forma se puede profundizar en el testeo de clases.

Recomiendo ver la documentación de la clase ReflectionClass para ver todas sus posibilidades, lo mostrado aquí es una pequeña parte.

Código en GitHub

miércoles, 20 de julio de 2016

Clases anónimas en php7

Ya ha sido liberado php7, lo que implica una renovación del lenguaje. Una de las novedades que trae son las clases anónimas.

¿Qué es una clase anónima? Simplificando, es una clase que no existe como tal pero que puede instanciar un objeto. Esto quiere decir que podemos crear un objeto a partir de una clase sin nombre, que no ha sido previamente creada.

¿Para qué nos puede servir? Veámoslo con un ejemplo.

Tenemos la siguiente clase Contract que tiene una variable de clase privada llamada $contractDate.
[code]class Contract { private $contractDate; private $contractMonths; public function __construct( string $date, int $contractMonths = 12) { $this->contractMonths = $contractMonths; $this->contractDate = new DateTime($date); } public function getEndDate() { (...) } (...) }[/code]
La variable $contactDate representa una fecha, es decir, tenemos como opciones: un String, un objeto DateTime (mejor), …

Vemos que el constructor de la clase Contract recibe dos parámetros: un String con la fecha y un integer con el número de meses de duración del contrato. Para manejar fechas lo más seguro es utilizar la propia clase de php DateTime, pero la fecha de finalización del contrato se calcularía en un método de la Clase contract getEndDate(), y esta podría guardarse en una variable de clase $endDate o devolverse calculada al vuelo en un método.

Una tercera opción sería pensar que, si la responsabilidad de las fechas está en la clase DateTime, ¿por qué no darle esa responsabilidad? Pues ya está, creemos la clase ContractDate que extendería DateTime, añadiendo sólo un método llamado getEndDate(). Tendríamos un nuevo fichero (un clase por fichero como buena práctica) con la clase ContractDate que sólo vamos a utilizar en la clase Contract pero estaría disponible en toda la aplicación. Aquí entran en juego las clases anónimas para evitar dicha situación y poder extender DateTime sin generar nuevos ficheros.

Modificamos el constructor.
[code hl="11"]class Contract { private $contractDate; const DEFAULT_CONTRACT_MONTHS = 12; public function __construct( string $date, int $contractMonths = self::DEFAULT_CONTRACT_MONTHS) { $this->contractMonths = $contractMonths; $this->contractDate = new class() extends DateTime { public function getEndDate( int $months, string $dateFormat = 'Y-m-d') : string { $date = new DateTime($this->format('Y-m-d')); $date->add(new DateInterval("P".$months."M")); $date->sub(new DateInterval("P1D")); return $date->format($dateFormat); } }; } }[/code]
Vemos que al inicializar $this->contractDate en el constructor escribimos new Class() extends DateTime { … }. Ahora tenemos un objeto de tipo clase anónima, pero a su vez es de tipo DateTime.

Ahora el método getEndDate() en la clase Contract ya no sería necesario si no queremos exponer ese dato fuera de la clase Contract.

Añadimos nuevos métodos públicos a la clase Contract. Uno de ellos se encargará de utilizar la fecha de finalización.
[code hl="13"]public function changeContractMonths(int $months) { $this->contractMonths = $months; } public function printStartDate() { echo("Start Date is ".$this->contractDate->format('Y-m-d').PHP_EOL); } public function printEndDate() { echo("End Date is ".$this->contractDate->getEndDate($this->contractMonths).PHP_EOL); }[/code]
De esta forma hemos visto como:
  • Crear un objeto a partir de una clase que no existe (anónima).
  • Extender una clase existente sin necesidad de crear una nueva clase.

Si añadimos la función notifyEach2Weeks5Times() vemos que el objeto generado a partir de la clase anónima se sigue comportando como un objeto de tipo DateTime.
[code]public function notifyEach2Weeks5Times() { $interval = new DateInterval('P2W'); $period = new DatePeriod($this->contractDate, $interval, 5, DatePeriod::EXCLUDE_START_DATE); foreach ($period as $nextDateTime) { echo $nextDateTime->format('Y-m-d').PHP_EOL; } }[/code]
Probamos la clase Contract con el código:
[code] echo("Contrato de 12 meses desde la fecha actual.".PHP_EOL); $contract = new Contract(date("Y-m-d"), 12); echo($contract->printStartDate()); echo($contract->printEndDate()); echo("Cambiamos los meses de contrato a 24.".PHP_EOL); $contract->changeContractMonths(24); echo($contract->printEndDate()); echo PHP_EOL; echo("Recordatorio cada 2 semanas 5 veces:".PHP_EOL); $contract->notifyEach2Weeks5Times(); [/code]
El resultado obtenido es:
[code]Contrato de 12 meses desde la fecha actual. Start Date is 2015-12-17 End Date is 2016-12-16 Cambiamos los meses de contrato a 24. End Date is 2017-12-16 Recordatorio cada 2 semanas 5 veces: 2015-12-31 2016-01-14 2016-01-28 2016-02-11 2016-02-25[/code]
Fichero en Git Hub

viernes, 1 de julio de 2016

¿Por qué hacer crecer la blogosfera?

¿Por qué un blog? ¿Con qué fin embarcarse en un proyecto que, con toda seguridad, no reportará beneficios al autor ni nada nuevo al mundo del desarrollo pero sí consumirá tiempo? Estas son las preguntas que seguro se habrá formulado todo aquel que haya pensado hacer algo parecido.

Podría empezar diciendo, en base a lo que cree mucha gente, que es por un ego necesitado de darse a conocer, de tener visitantes, de revolucionar el mundo del desarrollo, de ... pero me estaría engañando a mi mismo. No, la respuesta es más simple de lo que parece, todo se resume con una frase que ya muchos han debido escuchar: "si no mejoras, empeoras".

Por tanto, para ser claros, este blog no pretende, en ningún caso, ni mucho menos de forma premeditada, dar grandes lecciones de cómo desarrollar, ni mostrar nuevas técnicas que no estén en otros sitios, ni ser un lugar de referencia. El único fin es que el autor del propio blog aprenda o repase o refresque o fije conceptos que con el tiempo se acaban diluyendo. Si de paso alguien encuentra ayuda a sus problemas, que serán parecidos a los del autor, eso que sacamos todos de provecho.

Cuando leemos un libro (recomiendo hacerlo de vez en cuando) o blog sobre algún nuevo tema que desconocíamos tendemos a tratar de aprender de forma rápida, sin pausar ni digerir, a fin de cuentas según lo tenemos delante lo vamos entendiendo, por tanto, siempre, la asimilación no llega a producirse cómo debiera y el olvido llega un día y ni nos enteramos. ¿Qué mejor forma de asimilar lo aprendido que explicando a otra persona ese algo nuevo que hemos descubierto? Ese es, precisamente, el fin de este blog.

Empecemos pues.