El objetivo del patrón de comando es aislar distintas funcionalidades en un objeto (el comando), por lo que puede reutilizarse en otros objetos (los comandantes). Generalmente, el Comandante también pasa un Receptor al Comando, p. un objeto al cual el comando está dirigido. Por ejemplo:
$car = new Car;
echo $car->getStatus(); // Dirty as Hell
$carWash = new CarWash;
$carWash->addProgramme('standard',
new CarSimpleWashCommand,
new CarDryCommand,
new CarWaxCommand);
$carWash->wash();
echo $car->getStatus(); // Washed, Dry and Waxed
En el ejemplo anterior, CarWash es el Commander. El automóvil es el receptor y el programa son los comandos reales. Por supuesto, podría haber tenido un método doStandardWash() en CarWash e hice de cada comando un método en CarWash, pero eso es menos extensible. Tendría que agregar un nuevo método y comando cada vez que quisiera agregar nuevos programas. Con el patrón de comandos, simplemente puede pasar en nuevos comandos (piensa devolución de llamada) y crear nuevas combinaciones fácilmente:
$carWash->addProgramme('motorwash',
new CarSimpleWashCommand,
new CarMotorWashCommand,
new CarDryCommand,
new CarWaxCommand);
Por supuesto, se puede usar cierres o funtores de PHP para esto también, pero vamos a seguir a la POO para este ejemplo. Otra cosa en la que los Comandos son útiles es cuando tienes más de un Comandante que necesita la funcionalidad del Comando, p.
$dude = new Dude;
$dude->assignTask('washMyCarPlease', new CarSimpleWashCommand);
$dude->do('washMyCarPlease', new Car);
Si hubiéramos hardcoded la lógica de lavado en el tren de lavado, que ahora tendría que duplicar todo el código en el Tío. Y como un Amigo puede hacer muchas cosas (porque es humano), la lista de tareas que puede hacer dará como resultado una clase terrible larga.
A menudo, el propio Commander también es un comando, por lo que puede crear un compuesto de comandos y apilarlos en un árbol. Los comandos a menudo también proporcionan un método de Deshacer.
Ahora, volviendo la vista a su LoginCommand, diría que no tiene mucho sentido hacerlo de esta manera. No tiene ningún objeto Command (es el alcance global) y su Command no tiene Receptor. En cambio, regresa al Comandante (lo que hace que el alcance global sea el Receptor). Entonces su comando realmente no funciona en el receptor. También es poco probable que necesite la abstracción en un Comando, cuando el inicio de sesión solo se realiza en un solo lugar. En este caso, estaría de acuerdo la LoginCommand está mejor situada en un adaptador de autenticación, tal vez con un patrón de estrategia:
interface IAuthAdapter { public function authenticate($username, $password); }
class DbAuth implements IAuthAdapter { /* authenticate against database */ }
class MockAuth implements IAuthAdapter { /* for UnitTesting */ }
$service = new AuthService();
$service->setAdapter(new DbAuth);
if($service->authenticate('JohnDoe', 'thx1183')) {
echo 'Successfully Logged in';
};
que podría hacerlo un poco más de comandos como:
$service = new LoginCommander;
$service->setAdapter(new DbAuth);
$service->authenticate(new User('JohnDoe', 'thx1138'));
if($user->isAuthenticated()) { /* ... */}
Usted podría agregue el método authenticate
al Usuario, por supuesto, pero luego deberá establecer el Adaptador de Base de Datos al Usuario para realizar la autenticación, por ej.
$user = new User('JohnDoe', 'thx1138', new DbAuth);
if ($user->authenticate()) { /* ... */ }
Eso sería posible también, pero personalmente, no veo por qué un usuario debería tener un adaptador de autenticación. No parece algo que un usuario debería tener. Un usuario tiene las credenciales requeridas por un adaptador de autenticación, pero no el adaptador en sí. Pasando el adaptador al método del usuario authenticate
sería una opción sin embargo:
$user = new User('JohnDoe', 'thx1138');
if ($user->authenticateAgainst($someAuthAdapter)) { /* ... */ }
Por otra parte, si está utilizando ActiveRecord, entonces su usuario tendrá conocimiento de la base de datos de todos modos y entonces usted podría simplemente volcar todo lo anterior y escribir todo el código de autentificación en el usuario.
Como puede ver, se reduce a cómo está configurando su aplicación. Y eso nos lleva al punto más importante: los Patrones de diseño ofrecen soluciones a problemas comunes y nos permiten hablar de ellos sin tener que definir toneladas de términos primero. Eso es genial, pero a menudo tendrás que modificar los patrones para que resuelvan tu problema concreto. Puede pasar horas teorizando sobre arquitectura y qué patrones usar y no habrá escrito un solo código. No piense demasiado sobre si un patrón es 100% fiel a la definición sugerida. Asegúrate de que tu problema esté resuelto.
lo explicaste muy bien, a veces necesito digerirlo. ¡GRACIAS! –
El concepto de "receptor" siempre queda fuera de otras explicaciones que he encontrado. Creo que hoy en día no enfatizamos el receptor en el mismo bloque de código ya que enfatizamos asynchronicity. Buena respuesta incluso 4 años después. – JoshuaDavid