Tell Don’t Ask en PHP

De las muchas técnicas y principios de POO que he aprendido en mi carrera, uno de los más útiles es el llamado Tell, Don’t Ask.
Para comprenderlo mejor, tenemos que contrastarlo con su anti patrón. Consideremos la siguiente clase:

PHP

Lo que la mayoría de los desarrolladores haría, es llenar esta clase de getters y setters:

PHP

Luego, en los controladores del framework de su elección, implementarán la lógica correspondiente. Veamos, por ejemplo, el caso de un cambio de contraseña.

PHP

Luego, un ejemplo de login:

PHP

Apuntando el Problema

Hay varios problemas con la lógica que acabamos de presentar.
Uno de los problemas más grandes es que nuestra clase Usuario se transforma en una clase bastante pobre: un mero contenedor de estado que puede cambiarse al antojo del desarrollador mediante los setters. En realidad, los métodos de la clase Usuario no reflejan ninguna acción ni intencionalidad del mundo real, que es uno de los principales propósitos de la Programación Orientada a Objetos.

El segundo problema, es que la clase que consume Usuario termina haciendo cosas que no le corresponden y esto termina por inflar sus líneas de código innecesariamente. Además, causa mucha repetición de código, como la lógica para definir si un password es válido. ¿Por qué el controlador debería hashear el password o verificar si son iguales? ¿Por qué debería lanzar excepciones? ¿Por qué la clase usuario no tiene esa responsabilidad?

El tercer problema es que la semántica del código no es clara, y esto lo hace más complejo de leer. set y get son palabras demasiado genéricas. ¿No sería mejor acaso tener métodos como User::login() y User::changePassword()?

Una mejor implementación

¿Qué pasaría si enriquecemos nuestra clase Usuario en términos de añadir comportamiento?

PHP

Lo que hicimos podemos resumirlo en tres sencillos pasos:

  1. Eliminamos todos los setters. Métodos que sólo setean propiedades sin proveer una acción clara de negocio son basura.
  2. Luego, encerramos toda la lógica de hashing y verificación de password en sus propios métodos, para reutilizarlos internamente.
  3. Creamos los métodos login y changePassword, que contienen la lógica que antes se estaba ejecutando en el controlador.

El resultado final, es mucho más legible y usable:

PHP

Esta es la idea central de Tell, Don’t Ask: cuando antes estábamos pidiendo (ask) por datos a la clase Usuario para realizar una acción fuera de la misma, y devolverle datos; lo que hacemos ahora es decirle (tell) a la clase Usuario lo que ésta debe hacer. Ella se actualiza a sí misma, porque se conoce mejor que nadie. Y si hay un error, nos lanzará una excepción que podemos manejar en otra capa de nuestra aplicación.

La Importancia del Dominio

Si nos fijamos bien, lo que hicimos fue mover lógica hacia nuestro núcleo, que es nuestro dominio (nuestros modelos). Mientras más ricos son nuestros modelos, más lógica podremos reutilizar. He aquí un principio que siempre debemos recordar: toda la lógica relevante al negocio debe estar en los modelos. Esto garantiza solidez y consistencia a toda la aplicación. Los modelos deben ser gordos y llenos de lógica, mientras que las capas de control y servicios deben ser pequeñas y utilizar la funcionalidad implementada por los modelos.

Esto reduce dramáticamente la duplicación de código y hace claras las acciones que podemos realizar en cada entidad de nuestra aplicación.
Este principio no es solo para seguirlo cuando hablamos de modelos, sino también de otros componentes de tu aplicación. Cuando te encuentres sacando información de alguna clase, haciendo algo con ella y luego volviéndola a poner en esa clase, refactoriza. Esa es una clara indicación de que la lógica debería vivir dentro de la clase que estás usando.

Leave a Reply

Your email address will not be published. Required fields are marked *