2010-07-09 5 views
8

Я сделал минималистский пример шаблона команды в PHP после того, как прочитал об этом. У меня есть несколько вопросов ...Вопросы о 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() vs, нуждающийся в создании объекта 1-го?

ответ

32

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

$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 является Командиром. Автомобиль - это приемник, а программа - это настоящие команды. Конечно, у меня мог бы быть метод doStandardWash() в CarWash и каждый из них использовал метод в CarWash, но это менее расширяемо. Мне нужно было бы добавить новый метод и команду всякий раз, когда я захочу добавить новые программы. С узором команды, я могу просто перейти в новых командах (думает, обратный вызов) и создавать новые комбинации легко:

$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); 

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

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

Теперь, оглядываясь назад на ваш LoginCommand, я бы сказал, что это не имеет смысла делать это таким образом. У вас нет объекта Command (это глобальная область), и ваша команда не имеет приемника. Вместо этого он возвращается в Commander (что делает глобальную область получателем). Таким образом, ваша команда не работает на приемнике. Также маловероятно, что вам понадобится абстракция в Команду, когда регистрация будет только когда-либо сделана в одном месте. В этом случае, я соглашаюсь 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% соответствует предложенному определению. Убедитесь, что ваша проблема решена.

+3

u очень хорошо объяснил, мне нужно когда-нибудь переварить его. БЛАГОДАРЯ! –

+1

Концепция «приемника» всегда исключена из других объяснений, которые я нашел. Я думаю, что в настоящее время мы не подчеркиваем приемник в том же блоке кода, что и подчеркиваем асинхронность. Хороший ответ даже спустя 4 года. – JoshuaDavid

Смежные вопросы