gpt4 book ai didi

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

转载 作者:可可西里 更新时间:2023-10-31 22:53:53 26 4
gpt4 key购买 nike

在阅读了相关内容后,我用 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需要先实例化一个对象?

最佳答案

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

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

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

当然,您也可以为此使用 PHP 的闭包或仿函数,但我们在此示例中坚持使用 OOP。命令派上用场的另一件事是,当您有多个需要命令功能的指挥官时,例如

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

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

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

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

如您所见,这归结为您设置应用程序的方式。这将我们带到了最重要的一点:设计模式为常见问题提供了解决方案,它们让我们无需先定义大量术语即可谈论这些问题。这很酷,但通常您必须修改模式才能使它们解决您的具体问题。您可以花费数小时对体系结构和使用哪些模式进行理论化,而您不会编写任何代码。不要过多考虑模式是否 100% 符合建议的定义。确保您的问题得到解决。

关于php - 关于命令模式(PHP)的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3209697/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com