Вопросы о шаблоне команд (PHP) - PullRequest
9 голосов
/ 09 июля 2010

Я сделал минималистичный пример Command Pattern в PHP после прочтения об этом.У меня есть несколько вопросов ...

Я хотел бы знать, правильно ли то, что я сделал?или, может быть, слишком минимальный, таким образом уменьшая точку шаблона команды

interface ICommand {
  function execute($params);
}
class LoginCommand implements ICommand {
  function execute($params) {
    echo "Logging in : $params[user] / $params[pass] <br />";
    $user = array($params["user"], $params["pass"]);
    // faked users data
    $users = array(
      array("user1", "pass1"),
      array("user2", "pass2")
    );

    if (in_array($user, $users)) {
      return true;
    } else {
      return false;
    }
  }
}

$loginCommand = new LoginCommand();
// $tries simulate multiple user postbacks with various inputs
$tries = array(
  array("user" => "user1", "pass" => "pass1"),
  array("user" => "user2", "pass" => "pass1"),
  array("user" => "user2", "pass" => "PaSs2")
);

foreach ($tries as $params) {
  echo $loginCommand->execute($params) ? " - Login succeeded!" : " - Login FAILED!";
  echo " <br />";
}

Мне интересно, есть ли какая-то разница от простого помещения этого LoginCommand в простую функцию, скажем, в классе Users?

, если LoginCommand лучше подходит для класса, не будет ли лучше, если бы это был статический класс, поэтому я могу просто вызвать LoginCommand::execute() против необходимости создания объекта 1st?

1 Ответ

34 голосов
/ 09 июля 2010

Смысл шаблона команд состоит в том, чтобы выделять отдельные функциональные возможности в объект (команду), поэтому его можно использовать повторно для нескольких других объектов (командиров). Обычно Командующий также передает Приемника Команде, например, объект, на который нацелена команда. Например:

$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

В приведенном выше примере, CarWash является Commander. Автомобиль - это Приемник, а программа - это фактические команды. Конечно, я мог бы иметь метод doStandardWash () в CarWash и сделать каждую команду методом в CarWash, но он менее расширяемый. Я должен был бы добавить новый метод и команду всякий раз, когда я хотел добавить новые программы. С помощью шаблона команд я могу просто передать новые команды (например, Callback) и легко создавать новые комбинации:

$carWash->addProgramme('motorwash',
                        new CarSimpleWashCommand, 
                        new CarMotorWashCommand,
                        new CarDryCommand, 
                        new CarWaxCommand);

Конечно, вы можете использовать для этого и замыкания или функторы PHP, но давайте в этом примере остановимся на ООП. Еще одна вещь, в которой Команды пригодятся, это когда у вас есть более одного Коммандера, которому требуется функциональность Команды, например,

$dude = new Dude;
$dude->assignTask('washMyCarPlease', new CarSimpleWashCommand);
$dude->do('washMyCarPlease', new Car);

Если бы мы жестко запрограммировали логику стирки в CarWash, теперь нам пришлось бы дублировать весь код в Dude. А поскольку чувак может делать много вещей (потому что он человек), список задач, которые он может выполнить, приведет к ужасному длинному классу.

Зачастую сам Командующий также является Командой, поэтому вы можете создавать Композитные Команды и складывать их в дерево. Команды также часто предоставляют метод отмены.

Теперь, оглядываясь назад на вашу LoginCommand, я бы сказал, что не имеет смысла делать это таким образом. У вас нет объекта Command (это глобальная область), а у вашей команды нет Receiver. Вместо этого он возвращается к Командующему (что делает глобальную область Приемником). Таким образом, ваша команда на самом деле не работает на приемнике. Также маловероятно, что вам понадобится абстракция в Команде, когда вход в систему выполняется только в одном месте. В этом случае я бы согласился, что LoginCommand лучше разместить в адаптере аутентификации, возможно, с шаблоном стратегии:

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';
};

Вы можете сделать это несколько более похожим на команду:

$service = new LoginCommander;
$service->setAdapter(new DbAuth);
$service->authenticate(new User('JohnDoe', 'thx1138'));
if($user->isAuthenticated()) { /* ... */}

Конечно, вы можете добавить метод authenticate к пользователю, но тогда вам придется установить адаптер базы данных на пользователя для выполнения аутентификации, например,

$user = new User('JohnDoe', 'thx1138', new DbAuth);
if ( $user->authenticate() ) { /* ... */ }

Это тоже возможно, но лично я не понимаю, почему у пользователя должен быть адаптер аутентификации. Это не похоже на то, что должен иметь пользователь. У пользователя есть учетные данные, требуемые адаптером аутентификации, но не сам адаптер. Передача адаптера пользовательскому методу authenticate будет возможной, хотя:

$user = new User('JohnDoe', 'thx1138');
if ( $user->authenticateAgainst($someAuthAdapter) ) { /* ... */ }

Опять же, если вы используете ActiveRecord, тогда ваш пользователь все равно узнает о базе данных, а затем вы можете просто сбросить все вышеперечисленное и записать весь код аутентификации в пользователя.

Как видите, все сводится к тому, как вы настраиваете свое приложение. И это подводит нас к самому важному моменту: шаблоны проектирования предлагают решения общих проблем, и они позволяют нам говорить об этом без необходимости сначала определять тонны терминов. Это круто, но часто вам придется изменять шаблоны, чтобы они решали вашу конкретную проблему. Вы можете часами теоретизировать об архитектуре и о том, какие шаблоны использовать, и вы не будете писать один код. Не думайте слишком много о том, является ли шаблон на 100% верным предложенному определению. Убедитесь, что ваша проблема решена.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...