繁体   English   中英

关于命令模式的问题(PHP)

[英]Questions about Command Pattern (PHP)

在阅读完之后,我在PHP中编写了一个简约的Command Pattern示例。 我有几个问题 ...

我想知道我做的是对的吗? 或者可能太小,从而减少了命令模式的要点

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需要实例化一个对象吗?

命令模式的要点是能够将不同的功能隔离到一个对象(命令)中,因此它可以在多个其他对象(指挥官)中重用。 通常,Commander还会将接收器传递给命令,例如命令所针对的对象。 例如:

$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是指挥官。 Car是接收器,程序是实际的命令。 当然我可以在CarWash中使用方法doStandardWash()并使每个命令成为CarWash中的一个方法,但这样做的可扩展性较差。 每当我想添加新程序时,我都必须添加一个新方法和命令。 使用命令模式,我可以简单地传入新命令(想想回调)并轻松创建新组合:

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

当然,你也可以使用PHP的闭包或仿函数,但是在这个例子中我们坚持使用OOP。 命令派上用场的另一件事是,当你有多个需要Command功能的Commander时,例如

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

如果我们将洗涤逻辑硬编码到CarWash中,我们现在必须复制Dude中的所有代码。 而且由于一个Dude可以做很多事情(因为他是人类),他可以做的任务列表将导致一个可怕的长课。

通常,Commander本身也是一个命令,因此您可以创建一个命令组合并将它们堆叠到树中。 命令通常也提供撤销方法。

现在,回顾一下你的LoginCommand,我会说这样做是没有多大意义的。 您没有Command对象(它是全局范围),并且您的Command没有Receiver。 相反,它返回到指挥官(使全球范围成为接收者)。 因此,您的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,那么您的用户无论如何都会知道数据库,然后您可以简单地转储上述所有内容并将完整的验证代码写入用户。

如您所见,它归结为您如何设置应用程序。 这就把我们带到了最重要的一点:设计模式为常见问题提供了解决方案,让我们可以在不必先定义大量术语的情况下谈论这些问题。 这很酷,但通常,您必须修改模式以使它们解决您的具体问题。 您可以花几个小时来理解架构和使用哪些模式,而您不会编写单个代码。 不要过多考虑模式是否与建议的定义完全一致。 确保您的问题得到解决。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM