2010-07-09 14 views
8

Wykonałem minimalistyczny przykład Command Command w PHP po przeczytaniu o tym. Mam kilka pytań ...Pytania dotyczące Command Pattern (PHP)

Chciałbym wiedzieć, czy to, co zrobiłem, jest słuszne? a może też minimalne, zmniejszając w ten sposób punkt wzór komend

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 />"; 
} 

Zastanawiam się, czy istnieje jakakolwiek różnica od po prostu umieszczenie tej LoginCommand do prostej funkcji powiedzmy w klasie Users?

czy lepiej pasuje do klasy, czy nie byłoby lepiej, gdyby była to klasa statyczna, więc mogę po prostu zadzwonić pod numer LoginCommand::execute() kontra kto musi zainicjować obiekt pierwszy?

Odpowiedz

32

Punkt wzorca polecenia jest w stanie wyizolować różne funkcje do obiektu (polecenia), dzięki czemu może być ponownie użyty przez wiele innych obiektów (dowódców). Zwykle Dowódca przekazuje również Odbiorcę do dowództwa, np. obiekt, na który polecenie jest kierowane. Na przykład:

$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 

W powyższym przykładzie CarWash jest dowódcą. Samochód to Odbiornik, a program to rzeczywiste Polecenia. Oczywiście mogłem mieć metodę doStandardWash() w CarWash i uczynić każde polecenie metodą w CarWash, ale jest to mniej rozszerzalne. Gdybym chciał dodać nowe programy, musiałbym dodać nową metodę i polecenie. Z wzoru polecenia, mogę po prostu przejść na nowe polecenia (myśleć oddzwaniania) i tworzyć nowe kombinacje łatwo:

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

Oczywiście, można użyć zamknięć lub funktory PHP dla tego też, ale trzymajmy się OOP dla tego przykład. Kolejną rzeczą, która przydaje się przy korzystaniu z Poleceń, jest posiadanie więcej niż jednego Dowódcy, który potrzebuje funkcji Dowodzenia, np.

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

Gdybyśmy zakodowali logikę myjącą w CarWash, musielibyśmy teraz skopiować cały kod w Dude. A ponieważ koleś może robić wiele rzeczy (ponieważ jest człowiekiem), lista zadań, które może wykonać, spowoduje straszliwą długą klasę.

Często sam Dowódca jest także Dowództwem, więc można utworzyć Złożoną Komendę i ułożyć je w drzewo. Polecenia często udostępniają również metodę cofania.

Teraz, patrząc wstecz na twoją LoginCommand, powiedziałbym, że nie ma sensu robić tego w ten sposób. Nie masz obiektu Command (to zasięg globalny), a Command nie ma odbiornika. Zamiast tego wraca do Commander (co powoduje, że zasięg globalny to Receiver). Więc twoje polecenie naprawdę nie działa na Odbiorniku. Jest również mało prawdopodobne, że będziesz potrzebował abstrakcji do polecenia, gdy logowanie będzie wykonywane tylko w jednym miejscu. W tym przypadku, zgodziłbym LoginCommand jest lepiej przygotowane do adaptera uwierzytelniania może wzorkiem strategii:

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'; 
}; 

Można to zrobić nieco więcej poleceń jak:

$service = new LoginCommander; 
$service->setAdapter(new DbAuth); 
$service->authenticate(new User('JohnDoe', 'thx1138')); 
if($user->isAuthenticated()) { /* ... */} 

mogłeś oczywiście należy dodać do użytkownika metodę authenticate, ale wówczas konieczne będzie ustawienie adaptera bazy danych w celu uwierzytelnienia użytkownika, np.

$user = new User('JohnDoe', 'thx1138', new DbAuth); 
if ($user->authenticate()) { /* ... */ } 

Byłoby to również możliwe, ale osobiście nie widzę powodu, dla którego użytkownik powinien mieć adapter uwierzytelniania. To nie brzmi jak coś, co powinien mieć użytkownik. Użytkownik ma poświadczenia wymagane przez adapter uwierzytelniania, ale nie sam adapter. Przechodząc adapter do authenticate metody użytkownika byłaby opcja choć:

$user = new User('JohnDoe', 'thx1138'); 
if ($user->authenticateAgainst($someAuthAdapter)) { /* ... */ } 

Potem znowu, jeśli używasz ActiveRecord, wówczas użytkownik będzie wiedział o bazy danych i tak i wtedy można po prostu zrzucić wszystko powyższe i pisać cały kod uwierzytelniania do użytkownika.

Jak widać, sprowadza się to do sposobu konfigurowania aplikacji. I to prowadzi nas do najważniejszego punktu: Wzorce projektowe oferują rozwiązania typowych problemów i pozwalają nam mówić o nich bez konieczności definiowania ton pierwszych terminów. To fajne, ale często trzeba będzie zmodyfikować wzorce, aby rozwiązać konkretny problem. Możesz spędzić godziny teoretyzując o architekturze i wzorcach, których używasz, a nie napisałeś ani jednego kodu. Nie myśl zbyt wiele o tym, czy wzór jest w 100% zgodny z sugerowaną definicją. Upewnij się, że problem został rozwiązany.

+3

Wyjaśniłeś bardzo dobrze, muszę kiedyś to przetrawić. DZIĘKI! –

+1

Pojęcie "odbiornika" jest zawsze pomijane przez inne wyjaśnienia, które znalazłem. Myślę, że obecnie nie kładziemy nacisku na odbiorcę w tym samym bloku kodu, ponieważ kładziemy nacisk na asynchroniczność. Dobra odpowiedź nawet 4 lata później. – JoshuaDavid

Powiązane problemy