Volvemos otra vez a la carga refactorizando nuestra aplicación php legacy. Esta es la serie de posts más larga que he hecho hasta el momento, pero creo que el tema lo merece.
Esta entrega empezaremos ha plantear la arquitectura de la aplicación, o al menos una separación lógica de clases que irá evolucionando. Nuestra arquitectura debe centrarse en la lógica de negocio. Alrededor de esta tendremos varios módulos “auxiliares” que tienen diversos fines.
Por ejemplo conectar al usuario con la aplicación, en nuestro caso será la linea de comandos. Por otro lado tenemos la persistencia, que en nuestro caso no nos afecta ya que no utilizamos nada de este tipo. También tenemos las factorías y constructores y por ultimo están las clases que representan la entrada al sistema. En nuestra aplicación de Trivial podría considerarse la clase GameRunner de este tipo.
La idea que debemos llevarnos de todo esto es que la lógica de negocio es lo más importante de nuestra aplicación.
Principio de inversión de dependencias
Estas premisas definen el principio.
– A. Los módulos de alto nivel no deberían depender de los de bajo nivel. Ambos deberían depender de abstracciones.
– B: Las abstracciones no deberían depender de los detalles. Son los detalles los que deberían depender de abstracciones.
Este es la D de SOLID, y básicamente el concepto es el siguiente: en orientación a objetos, lo normal es tener una jerarquía de objetos que se unen porque los de más alto nivel suelen incluir una instancia de los de más bajo nivel. Por ejemplo, un bosque contiene árboles, que a su vez contienen hojas, que contienen células…
Es decir, tener una base de datos MySQL es muy concreto, por lo que se debería depender de una clase más abstracta. La interfaz web es algo muy concreto y debe depender de algo más abstracto. Así pues la lógica de negocio debe representar de manera abstracta los procesos y comportamientos de un dominio especifico.
Ahora vamos ir materializando este concepto es algo más concreto.
El truco técnico
Tenemos que el principio de inversión de dependencias dice que debemos invertir nuestras dependencias cada vez que tengamos código que depende de algo concreto.
GameRunner crea una clase Game y la usa. Por otro lado, tenemos nuestra clase Game que, de momento representa “toda” nuestra lógica de negocio, cea y utiliza un objeto de la clase Display.
Vamos a empezar por lo más simple que podemos hacer y es utilizar clases abstractas. ¿Por qué?
Que Game dependa de Display no es bueno, nuestra capa de presentación es muy concreta y tenemos que crear algo para abstraerlo.
Tenemos que una clase normal es más concreta que una clase abstracta que es mas concreta que una interfaz. Así que vamos a ir paso a paso para crear nuestra clase abstracta para de abstrayendo nuestra lógica de negocio.
Una clase abstracta, sin entrar en muchos detalles, es un tipo especial de clase que contiene definiciones de métodos e implementaciones parciales. Generalmente una clase abstracta tiene una o varias clases hijas que heredan la funcionalidad parcial común del padre y que implementan los métodos abstractos con un comportamiento propio.
Una interfaz solo permite la definición de los métodos y las variables.
En PHP no se permite herencia múltiple. Así que una clase concreta solo puede extender de una clase abstracta, pero puede implementar varias interfaces.
Inversión de dependencias utilizando una interfaz
Si vemos los métodos de la clase Display tenemos unos cuantos métodos públicos, quizás demasiados. Lo ideal es tener una interfaz lo más pequeña y usable posible exponiendo sólo los métodos que realmente se necesitan, pero de momento esto no será un problema. Nuestro objetivo en esta serie de tutoriales es refactorizar una aplicación legacy/obsoleta escrita en PHP. Así que optamos por tener una arquitectura más o menos así:
De esta manera conseguiremos que Game dependa de algo abstracto, de nuestra interfaz, para que sea la clase Display la que implemente dicha interfaz.
Naming
Ya hemos hablado de esto del naming otras veces, tener un buen naming es indispensable para mejorar la mantenibilidad del código. Dar un buen nombre es algo esencial. Podríamos empezar por nombrar nuestra interfaz como IDisplay (Interfaz Display), así sabremos que es una interfaz.
El problema es que dependiendo estamos “matando” el dominio del problema, ya que nuestra interfaz se relaciona con Game. A la clase Game no le importa si se está relacionando con una interfaz o con un objeto real. Desde el punto de vista de Game, solo se utiliza Display y punto.
Podemos llamar a nuestra interfaz Display y a la clase que implementa dicha interfaz DisplayImpl. Esto soluciona el problema anterior con el nombre de la interfaz. Además si llamamos a nuestra interfaz Display vamos a tener que realizar muy pocos cambios en la clase Game. Pero tener una clase que tenga como sufijo Impl es algo feo. Cuando alguien pregunte, bueno y por donde es posible mostrar el juego, no sabremos contestar, no sabemos que cual es la salida de nuestro juego con tan solo mirar el nombre de la clase.
Ahora quizás este considerablemente mejor 🙂 Nuestra implementación se llamará CLIDisplay, que indica que nuestra salida será a través de un terminal. Si más adelante necesitamos crear una nueva salida para nuestro Game podemos crear una nueva clase que implemente una interfaz.
Empezando a programar
Como ya sabemos, debemos ir dando “baby steps” haciendo mínimos cambios y probando que todo funciona. Además debemos cuidar que nuestros tests no se están quedando atrás.
Los test unitarios deberían probar clases unitarias y que el acoplamiento entre las clases sea el menor posible, así que lo que tenemos que hacer es Mocking. A grandes rasgos, según la Wikipedia Mocking es un objeto que imita o simula el comportamiento de un objeto real de forma controlada.
Pensándolo un poco, quizás ni siquiera necesitemos un Mock, sino solo un Stub. Es decir solo necesitamos un objeto “tonto” que podamos utilizar.
Creando la interfaz
Lo primero que haremos será cambiar el nombre de nuestra clase Display de manera temporal. Así evitaremos tener 2 archivos que se llamen igual. A nuestro archivo Display lo llamaremos DisplayRefactoringProcess y crearemos un nuevo archivo llamada Display
En la interfaz solo nos quedamos con los métodos públicos, llegando a algo como esto:
interface Display
{
public function statusAfterRoll($rolledNumber, $currentPlayer);
public function playerGettingOutOfPenaltyBox($currentPlayer);
public function playerNewLocation($currentPlayer, $currentPlaces);
public function currentCategory($currentCategory);
public function playerStaysInPenaltyBox($currentPlayer);
public function askQuestion($currentCategory);
public function correctAnswer();
public function correctAnswerWithTypo();
public function playerCoins($currentPlayer, $playerCoins);
public function incorrectAnswer();
public function playerSentToPenaltyBox($currentPlayer);
public function playerAdded($playerName, $numberOfPlayers);
}
Hemos llegado a un punto donde ya tenemos responsabilidades separadas. Nuestra clase Game ahora depende de un puñado de métodos abstractos, en lugar de depender de algo concreto. Aunque de momento si intentamos ejecutar las pruebas, nos llevaremos un fiasco, porque siguen en rojo.
Siguen en rojo porque tenemos que crear una clase que implemente la interfaz Display.
Dependencia de Inyección
Necesitamos un objeto “Dummy” para poder utilizarlo en nuestras pruebas. Para crear este objeto podemos:
- utilizar una clase vacía que implemente la interfaz Display pero que los métodos no hagan nada.
- Seguir utilizando la clase DisplayRefactoringProcess.
- Utilizar los Mocks de PHPUnit o alguna librería similar para mockear la clase.
Pero antes debemos inyectar dependecias en el constructor de la clase Game para que nuestra lógica de negocio no dependa de la interfaz de usuarios. Es decir, necesitamos hacer inyección de dependencias para poder hacer test y tener código de producción. Para ello tenemos que modificar el constructor de la clase Game así:
function __construct(Display $display)
{
$this->players = array();
$this->places = array(0);
$this->purses = array(0);
$this->inPenaltyBox = array(0);
$this->display = $display;
}
Ahora debemos escoger una de las 3 soluciones de arriba para modificar los tests. Nosotros vamos a escoger utilizar los Mocks de PHPunit, por lo que tenemos que modificar nuestro metodo setUp() de la clase GameTest
public function setUp()
{
$mockDipslay = $this->getMock('\GameLegacy\Display');
$this->_game = new Game($mockDipslay);
}
Ahora podemos ejecutar nuestros tests y estos seguirán estando en verde con solo modificar una sola linea de los mismos.
A por la implementación real
Ahora debemos terminar la refactorización que tenemos a medias. Si recordáis modificamos el nombre de la clase Display por DisplayRefactoringProcess, como bien sabemos este nombre no es nada bueno así que vamos a cambiarlo por algo como CLIDisplay ya que indica que esa clase tiene como salida el terminal.
class CLIDisplay implements Display
Y ahora tenemos que hacer que la función run() de GameRunner utilice la inyección de dependencias y por tanto utilice nuestra nueva clase CLIDisplay.
function run()
{
$display = new CLIDisplay();
$aGame = new Game($display);
$aGame->add("Chet");
$aGame->add("Pat");
$aGame->add("Sue");
do {
$dice = rand(0, 5) + 1;
$aGame->roll($dice);
} while (didSomeoneWin($aGame, isCorrectAnswer()));
}
Y listo, ya tenemos una interfaz que nos ayuda a separar las dependencias, además podemos ampliar nuestro juego de trivial creando una clase JSON por ejemplo que devuelva la salida como un json o lo que queramos.
Conclusiones
Ahora tenemos que GameRunner es nuestro punto de entrada a la aplicación, es él el que crea un CLIDisplay concreto y lo usa. CLIDisplay solo depende de la interfaz, es el contrato que debe cumplir. Ahora empezamos a tener una arquitectura limpia, que nos ayuda a extender nuestro proyecto y a que este sea mantenible.
Este post esta basado en la serie http://code.tutsplus.com/tutorials/refactoring-legacy-code-part-8-inverting-dependencies-for-a-clean-architecture–cms-21659.