2010-01-31 25 views
7

Ho una domanda che è un po 'più una domanda di design. Ho una classe che definisce un sacco di funzioni. Tuttavia, voglio che il comportamento di alcune funzioni sia modificabile durante il runtime - o almeno funzioni come un'interfaccia plugin durante la fase di progettazione. es:Classi PHP polimorfici/plug-in

class MyClass { 
    public function doSomething(); 
} 

$obj = new MyClass(); 
// PSEUDO CODE 
$obj->doSomething = doSomethingElse; 
$obj->doSomething(); // does something else 

Ho avuto varie idee su come implementare qualcosa di simile nel codice "reale". Tuttavia, non ne sono del tutto sicuro, qual è la strada giusta da percorrere. Per prima cosa ho pensato che potrei usare un'interfaccia per questo scopo.

interface IDoSomethingInterface { 
    public function doSomething(); 
} 

class DoSomethingClass implements IDoSomethingInterface 
{ 
    public function doSomething() 
    { 
    // I do something! 
    } 
} 

class DoSomethingElseClass implements IDoSomethingInterface 
{ 
    public function doSomething() 
    { 
    // I actually do something else! 
    } 
} 

class MyClass { 
    public doSomething(IDoSomething $doSomethingProvider) 
    { 
    $doSomethingProvider->doSomething(); 
    } 
} 

$obj = new MyClass(); 
$doSomethingProvider = new DoSomethingClass(); 
$doSomethingElseProvider = new DoSomethingElseClass(); 

$obj->doSomething($doSomethingProvider); // I do something! 
$obj->doSomething($doSomethingElseProvider); // I do something else! 

Questo approccio potrebbe essere esteso per non avere il provider doSomething passato come parametro, ma per impostarlo sia membro come oggetto o membro della classe. Tuttavia, non mi piaceva che dovessi creare un'istanza di n classe che non contenesse nemmeno una singola variabile membro e l'unico metodo potrebbe facilmente essere una funzione di classe statica. A questo punto non c'è più bisogno di un oggetto. Poi ho provato a utilizzare le variabili di funzione, ma non sarei riuscito a rientrare in OOP, cosa che a me non piaceva. Le mie domande sono: qual è il modo migliore per implementare un sistema come quello che ho descritto? Quale approccio vorresti provare o usare? C'è qualcosa di ovvio a cui non avrei pensato? O dovrei semplicemente andare con l'approccio dell'interfaccia e la mia cattiva sensazione di instanciare oggetti "senza corpo" è solo una preziosa nullità ?! Sono curioso delle tue risposte!

Edit:

Perché io sono veramente interessato a chiarire se questo in realtà è (o dovrebbe essere) uno Stato o di un modello di strategia, andrò in qualche modo più dettagliato sull'attuazione. Ho una classe base di collezioni da cui ricavo diverse implementazioni specifiche. Voglio essere in grado di selezionare un singolo elemento da qualsiasi specifica implementazione - tuttavia, voglio essere in grado per modificare dinamicamente come questo elemento è selezionato dalla collezione, ad esempio:

class MyCollection implements Countable, ArrayAcces, Iterator 
{ 
    // Collection Implementation 
    public function select(ISelectionStrategy $strategy) 
    { 
    return $strategy->select($this); 
    } 
} 

interface ISelectionStrategy 
{ 
    public function select(MyCollection $collection); 
} 

class AlphaSelectionStrategy implements ISelectionStrategy 
{ 
    public function select(MyCollection $collection); 
    { 
    reset($collection); 
    if (current($collection)) 
     return current($collection); 
    else 
     return null; 
    } 
} 

class OmegaSelectionStrategy implements ISelectionStrategy 
{ 
    public function select(MyCollection $collection) 
    { 
    end($collection); 
    if (current($collection)) 
     return current($collection) 
    else 
     return null; 
    } 
} 

class RandomSelectionStrategy implements ISelectionStrategy 
{ 
    public function select(MyCollection $collection) 
    { 
    if (!count($collection)) 
     return null; 
    $rand = rand(0, count($collection)-1); 
    reset($collection); 
    while($rand-- > 0) 
    { 
     next($collection); 
    } 
    return current($collection); 
    } 
} 

$collection = new MyCollection(); 
$randomStrategy = new RandomSelectionStrategy(); 
$alphaStrategy = new AlphaSelectionStrategy(); 
$omegaStrategy = new OmegaSelectionStrategy(); 

$collection->select($alphaStrategy); // return first element, or null if none 
$collection->select($omegaStrategy); // return last element, or null if none 
$collection->select($randomStrategy); // return random element, or null if none 

Questo è fondamentalmente ciò che voglio raggiungere . Si tratta ora di una implementazione del modello di strategia o del modello di stato, anche se ho usato il termine strategia perché si adatta di più in questo caso comunque. Per quanto ne so, la strategia e lo schema di stato sono fondamentalmente uguali, tranne che il loro intento è diverso. Il collegamento fornito da Gordon afferma che l'intenzione di un modello di stato è "Permetti ad un oggetto di alterare il suo comportamento quando il suo stato interno cambia" - ma questo non è il caso qui. Quello che voglio è essere in grado di dire alla mia classe MyCollection "usa questo o quell'algoritmo per darmi un elemento" - non "dammi un elemento usando un algoritmo che determini attraverso il tuo stato". Spero che qualcuno possa chiarire questo!

Con i migliori saluti, Daniel

+1

+1, quella faccina sarà la mia fine. xP –

+0

Bene, hai già risposto alla tua domanda. Non vuoi gestire gli stati. Vuoi scambiare singoli comportamenti, quindi la strategia è appropriata. Su un sidenote, dal momento che sembri essere tedesco, posso consigliare il libro phpdesignpatterns.de – Gordon

risposta

4

Il tuo approccio è corretto. Si tratta di una Strategy Pattern (UML diagram):

Partenza mia risposta a questa domanda (skip to in cui si dice Dal momento che si desidera modificare dinamicamente il comportamento):

An alternativa al tuo specifico UseCase sarebbe quello di raggruppare le strategie di selezione in una singola classe di servizio, invece di assegnarle alla tua classe di raccolta. Quindi, anziché passare le strategie alla raccolta, passare la raccolta alla classe di servizio, ad es.

class CollectionService implements ICollectionSelectionService 
{ 
    public static function select(MyCollection $collection, $strategy) { 
     if(class_exists($strategy)) { 
      $strategy = new $strategy; 
      return $strategy->select($collection); 
     } else { 
      throw new InvalidArgumentException("Strategy does not exist"); 
     }  
    } 
} 
$element = CollectionService::select($myCollection, 'Alpha'); 

Lascio a voi se utilizzare questo con metodi statici o meno. Per impedire l'istanziazione multipla delle strategie di selezione, è possibile memorizzare gli oggetti Selection all'interno del servizio per riutilizzarli.

Per gli altri modelli del comportamento controllare

+0

Grazie mille! Ora ho solo bisogno di schiacciare quel "Non .. Ho bisogno .. Oggetto .. Qui .. Urg .." - sentimento;) –

+0

A partire da 5.3, potresti usare anche chiusure per questo approccio, ma poi tu non è possibile digitare hint o programma su un'interfaccia più. – Gordon

+0

La strategia viene utilizzata per fornire algoritmi ottimizzati, come ad esempio la ricerca binaria quando si ha a che fare con la matrice ordinata invece della ricerca lineare per la matrice non ordinata. Inoltre, la selezione della strategia viene spesso implementata utilizzando metodi sovraccaricati, che non sono supportati in PHP (anche qui __call non fa un ottimo lavoro). Penso che lo schema di stato sia più appropriato in questo caso: applica l'implementazione corretta in base allo stato configurabile esternamente. –

3

La soluzione profilassi è più o meno a destra. Potresti non saperlo, ma questo è in realtà "State" Design Pattern. Lo stato del sistema è rappresentato dall'oggetto. Se è necessario modificare lo stato, è sufficiente sostituire l'oggetto con un altro stato.

alt text http://dofactory.com/Patterns/Diagrams/state.gif

Vi consiglio di stare con quello che hai, come è dimostrato da GoF essere soluzione praticabile.

+0

Anche grazie, credo che quel "modello di strategia" sia in realtà lo schema e il termine corretti. –

+0

Hey Dan, a questo punto probabilmente stiamo diventando troppo pedante, ma sono abbastanza sicuro che State è esattamente quello che stai cercando. L'intero punto di stato è che l'oggetto State (implementor) può essere impostato sull'oggetto master al di fuori di esso, mentre in Strategy la decisione su quale implementazione utilizzare viene eseguita all'interno dell'oggetto master senza alcun modo di cambiarla dall'esterno. Ad ogni modo, quello che hai al momento ha perfettamente senso. –

+0

In entrambi i casi, vale ancora la pena. Per me, i modelli Gang of four non sono rigidi mandati "fai-da-questo-via", ma suggeriscono che i buoni principi di progettazione devono essere applicati in modo flessibile. C'è comunanza tra lo stato e i modelli di strategia, dal momento che entrambi hanno implementazioni commutabili come un obiettivo chiave. Essere precisi su quale schema ha valore, ma le idee sottostanti sono il vero guadagno. – Steve314