2010-07-09 12 views
8

ho fatto un esempio modello di comando minimalista in PHP dopo aver letto fino a questo proposito. Ho un paio di domande ...Domande su modello di comando (PHP)

mi piacerà sapere se quello che ho fatto è giusto? o forse troppo minimal, riducendo così il punto del modello di comando

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

mi chiedo se non v'è alcuna differenza dal semplice mettere questo LoginCommand in una funzione semplice dire nella classe Users?

se LoginCommand è più adatto per una classe, non sarebbe meglio se fosse una classe statica, quindi posso semplicemente chiamare LoginCommand::execute() rispetto alla necessità di instanciare un oggetto 1?

risposta

32

Il punto del Pattern comando è in grado di isolare funzionalità distinta in un oggetto (comando), in modo che possono essere riutilizzati in più altri oggetti (i comandanti). Di solito, il comandante passa anche un ricevitore al comando, ad es. un oggetto a cui è indirizzato il comando. Ad esempio:

$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 

Nell'esempio precedente, CarWash è il comandante. La macchina è il ricevitore e il programma sono i comandi effettivi. Ovviamente avrei potuto avere un metodo doStandardWash() in CarWash e rendere ogni comando un metodo in CarWash, ma che è meno estendibile. Dovrei aggiungere un nuovo metodo e comando ogni volta che volevo aggiungere nuovi programmi. Con il modello di comando, posso semplicemente passare a nuovi comandi (si pensi richiamata) e creare nuove combinazioni facilmente:

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

Naturalmente, è possibile utilizzare le chiusure o funtori di PHP anche per questo, ma cerchiamo di attenersi a OOP per questo esempio. Un'altra cosa in cui i Comandi tornano utili, è quando hai più di un Comandante che ha bisogno della funzionalità Comando, ad es.

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

Se avessimo hardcoded la logica di lavaggio nel CarWash, che oggi hanno duplicare tutto il codice nella Amico. E dal momento che un Amico può fare molte cose (perché è umano), l'elenco dei compiti che può svolgere, si tradurrà in una terribile lunga lezione.

Spesso, il Comandante stesso è anche un comando, in modo da poter creare un composito di comandi e impilarli in un albero. I comandi spesso forniscono anche un metodo Annulla.

Ora, guardando indietro al vostro LoginCommand, direi che non ha molto senso per farlo in questo modo. Non hai un oggetto Comando (è l'ambito globale) e il tuo Comando non ha Ricevitore. Invece ritorna al Commander (che rende il ricevitore globale). Quindi il tuo comando non opera realmente sul ricevitore. È anche improbabile che tu abbia bisogno dell'astrazione in un comando, quando l'accesso avviene sempre in un unico posto. In questo caso, sarei d'accordo il LoginCommand è in una posizione migliore in un adattatore di autenticazione, magari con un modello di strategia:

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

si potrebbe fare un po 'più di comando simile:

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

Si potrebbe aggiungere il metodo authenticate all'Utente, naturalmente, ma poi si dovrà impostare l'adattatore database all'Utente per fare l'autenticazione, ad esempio,

Anche questo sarebbe possibile, ma personalmente, non vedo perché un utente debba avere un adattatore di autenticazione Non sembra qualcosa che un utente dovrebbe avere. Un utente ha le credenziali richieste da un adattatore di autenticazione, ma non l'adattatore stesso. Passando l'adattatore al metodo di authenticate dell'utente sarebbe un'opzione però:

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

Poi di nuovo, se si utilizza ActiveRecord, quindi l'utente sarà conoscere il database in ogni caso e quindi si può semplicemente scaricare tutto quanto sopra e scrivere l'intero codice di autenticazione nell'utente.

Come puoi vedere, si riduce a come stai configurando la tua applicazione. E questo ci porta al punto più importante: i Design Patterns offrono soluzioni a problemi comuni e ci permettono di parlare di questi senza dover definire prima un sacco di termini. È bello, ma spesso dovrai modificare i modelli per farli risolvere il tuo problema concreto. Puoi dedicare ore a teorizzare l'architettura e quali modelli usare e non avrai scritto un singolo codice. Non pensare troppo se un pattern è fedele al 100% alla definizione suggerita. Assicurati che il tuo problema sia risolto.

+3

hai spiegato molto bene, ho bisogno di un po 'di tempo per digerirlo. GRAZIE! –

+1

Il concetto di "destinatario" è sempre escluso da altre spiegazioni che ho trovato. Penso che al giorno d'oggi non si enfatizzi il ricevitore nello stesso blocco di codice, dato che enfatizziamo l'asincronia. Bella risposta anche 4 anni dopo. – JoshuaDavid