命令模式的要點是能夠將不同的功能隔離爲一個對象(命令),因此它可以在多個其他對象(指揮官)之間重複使用。通常,指揮官還將接收者傳遞給命令,例如,該命令針對的對象。例如:
$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是Receiver,程序是實際的命令。當然,我可以在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);
如果我們硬編碼洗滌邏輯到洗車場,我們現在可以複製所有代碼的老兄。而且由於老兄可以做很多事情(因爲他是人類),他能做的任務清單將導致可怕的長班。
通常,Commander本身也是一個Command,因此您可以創建Commands of Commands並將它們堆疊到樹中。命令通常也提供撤銷方法。
現在,回頭看看你的LoginCommand,我認爲這樣做沒有什麼意義。你沒有Command對象(它是全局作用域),你的Command沒有接收者。相反,它會返回到Commander(它使全球範圍成爲Receiver)。所以你的命令在Receiver上並不真正運行。在進行登錄操作只能在一個地方完成的情況下,你也不太可能需要抽象到Command中。在這種情況下,我會同意的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的,那麼你的用戶就會知道有關數據庫無論如何,然後你可以簡單地轉儲上述所有寫將整個認證碼輸入用戶。你可以看到,它歸結爲你如何設置你的應用程序。這使我們想到了最重要的一點:設計模式爲常見問題提供瞭解決方案,他們讓我們允許對這些問題進行討論,而無需首先定義大量術語。這很酷,但通常情況下,您將不得不修改模式以使其解決具體問題。你可以花幾個小時理論化架構,使用哪種模式,而且你不會編寫單一的代碼。不要過多考慮一個模式是否與建議的定義完全相符。確保你的問題得到解決。
你解釋得很好,我需要一段時間來消化它。謝謝! – 2010-07-09 11:07:28
「接收器」的概念總是被我發現的其他解釋所遺漏。我認爲現在我們不強調接收器在同一塊代碼中,因爲我們強調了異步性。甚至4年後的好回答。 – JoshuaDavid 2014-11-03 04:42:18