在阅读了相关内容后,我用 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/
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
尝试通过RVM将RubyGems升级到版本1.8.10并出现此错误:$rvmrubygemslatestRemovingoldRubygemsfiles...Installingrubygems-1.8.10forruby-1.9.2-p180...ERROR:Errorrunning'GEM_PATH="/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/ruby-1.9.2-p180@global:/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/rub
我想用ruby编写一个小的命令行实用程序并将其作为gem分发。我知道安装后,Guard、Sass和Thor等某些gem可以从命令行自行运行。为了让gem像二进制文件一样可用,我需要在我的gemspec中指定什么。 最佳答案 Gem::Specification.newdo|s|...s.executable='name_of_executable'...endhttp://docs.rubygems.org/read/chapter/20 关于ruby-在Ruby中编写命令行实用程序
鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende
我的最终目标是安装当前版本的RubyonRails。我在OSXMountainLion上运行。到目前为止,这是我的过程:已安装的RVM$\curl-Lhttps://get.rvm.io|bash-sstable检查已知(我假设已批准)安装$rvmlistknown我看到当前的稳定版本可用[ruby-]2.0.0[-p247]输入命令安装$rvminstall2.0.0-p247注意:我也试过这些安装命令$rvminstallruby-2.0.0-p247$rvminstallruby=2.0.0-p247我很快就无处可去了。结果:$rvminstall2.0.0-p247Search
由于fast-stemmer的问题,我很难安装我想要的任何rubygem。我把我得到的错误放在下面。Buildingnativeextensions.Thiscouldtakeawhile...ERROR:Errorinstallingfast-stemmer:ERROR:Failedtobuildgemnativeextension./System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin/rubyextconf.rbcreatingMakefilemake"DESTDIR="cleanmake"DESTDIR=
当我尝试安装Ruby时遇到此错误。我试过查看this和this但无济于事➜~brewinstallrubyWarning:YouareusingOSX10.12.Wedonotprovidesupportforthispre-releaseversion.Youmayencounterbuildfailuresorotherbreakages.Pleasecreatepull-requestsinsteadoffilingissues.==>Installingdependenciesforruby:readline,libyaml,makedepend==>Installingrub
我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www