Jie*_*eng 9 php oop design-patterns
在阅读完之后,我在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 />";
}
Run Code Online (Sandbox Code Playgroud)
我想知道是否有任何区别只是简单地把它LoginCommand放入一个简单的函数说在Users课堂上?
如果LoginCommand它更适合一个类,如果它是一个静态类不是更好,所以我可以简单地调用LoginCommand::execute()vs需要实例化一个对象吗?
Gor*_*don 34
命令模式的要点是能够将不同的功能隔离到一个对象(命令)中,因此它可以在多个其他对象(指挥官)中重用.通常,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
Run Code Online (Sandbox Code Playgroud)
在上面的例子中,CarWash是指挥官.Car是接收器,程序是实际的命令.当然我可以在CarWash中使用方法doStandardWash()并使每个命令成为CarWash中的一个方法,但这样做的可扩展性较差.每当我想添加新程序时,我都必须添加一个新方法和命令.使用命令模式,我可以简单地传入新命令(想想回调)并轻松创建新组合:
$carWash->addProgramme('motorwash',
new CarSimpleWashCommand,
new CarMotorWashCommand,
new CarDryCommand,
new CarWaxCommand);
Run Code Online (Sandbox Code Playgroud)
当然,你也可以使用PHP的闭包或仿函数,但是在这个例子中我们坚持使用OOP.命令派上用场的另一件事是,当你有多个需要Command功能的Commander时,例如
$dude = new Dude;
$dude->assignTask('washMyCarPlease', new CarSimpleWashCommand);
$dude->do('washMyCarPlease', new Car);
Run Code Online (Sandbox Code Playgroud)
如果我们将洗涤逻辑硬编码到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';
};
Run Code Online (Sandbox Code Playgroud)
你可以做一些类似命令:
$service = new LoginCommander;
$service->setAdapter(new DbAuth);
$service->authenticate(new User('JohnDoe', 'thx1138'));
if($user->isAuthenticated()) { /* ... */}
Run Code Online (Sandbox Code Playgroud)
您当然可以将该authenticate方法添加到用户,但是您必须将数据库适配器设置为用户才能进行身份验证,例如
$user = new User('JohnDoe', 'thx1138', new DbAuth);
if ( $user->authenticate() ) { /* ... */ }
Run Code Online (Sandbox Code Playgroud)
这也是可能的,但就个人而言,我不明白为什么用户应该拥有身份验证适配器.它听起来不像用户应该拥有的东西.用户具有身份验证适配器所需的凭据,但不是适配器本身.将适配器传递给用户的authenticate方法将是一个选项:
$user = new User('JohnDoe', 'thx1138');
if ( $user->authenticateAgainst($someAuthAdapter) ) { /* ... */ }
Run Code Online (Sandbox Code Playgroud)
然后,如果您使用的是ActiveRecord,那么您的用户无论如何都会知道数据库,然后您可以简单地转储上述所有内容并将完整的验证代码写入用户.
如您所见,它归结为您如何设置应用程序.这就把我们带到了最重要的一点:设计模式为常见问题提供了解决方案,让我们可以在不必先定义大量术语的情况下谈论这些问题.这很酷,但通常,您必须修改模式以使它们解决您的具体问题.您可以花几个小时来理解架构和使用哪些模式,而您不会编写单个代码.不要过多考虑模式是否与建议的定义完全一致.确保您的问题得到解决.