2010-01-31 11 views
7

Ich habe eine Frage, die eher eine Designfrage ist. Ich habe eine Klasse, die eine Reihe von Funktionen definiert. Ich möchte jedoch, dass das Verhalten einiger Funktionen während der Laufzeit veränderbar ist - oder zumindest während der Entwurfszeit wie eine Plugin-Schnittstelle funktioniert. zB:Polymorph/Plugable PHP Klassen

class MyClass { 
    public function doSomething(); 
} 

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

Ich hatte verschiedene Ideen, wie man sowas in "echtem" Code umsetzen kann. Ich bin mir jedoch nicht ganz sicher, welches der richtige Weg ist. Ich dachte zuerst, ich könnte ein Interface für diesen Zweck verwenden.

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! 

Dieser Ansatz könnte erweitert werden, nicht den doSomething Anbieter als Parameter übergeben haben, aber es entweder als Objekt Mitglied oder sogar Klassenmitglied einzustellen. Allerdings mochte ich nicht, dass ich eine Instanz von n-Klasse erstellen muss, die nicht einmal eine einzelne Elementvariable enthält, und die einzige Methode könnte einfach eine statische Klassenfunktion sein. An diesem Punkt brauchen wir kein Objekt. Ich wollte dann versuchen, Funktionsvariablen zu verwenden - aber ich würde dann aus OOP herausfallen, was ich auch nicht mochte. Meine Fragen sind: Was ist der beste Weg, um ein System wie ich beschrieben zu implementieren? Welchen Ansatz würden Sie verwenden? Ist etwas offensichtlich, an das ich vielleicht nicht gedacht hätte? Oder sollte ich einfach mit dem Interface-Ansatz gehen und mein schlechtes Gefühl, "körperlose" Objekte zu installieren, ist nur eine nerdige Kostbarkeit ?! Ich bin neugierig auf deine Antworten!

Edit:

Weil ich wirklich interessiert bin zu klären, ob dies tatsächlich ist (oder sein soll) ein Staat oder ein Strategy-Muster, ich werde über die Umsetzung in etwas mehr Detail gehen. Ich habe eine Basisklasse von Sammlungen, aus denen ich verschiedene spezifische Implementierungen ableite. Ich möchte in die Lage, ein einzelnes Element aus jeder spezifischen Implementierung wählen - aber ich will dynamisch können, ändern, wie dieses Element aus der Sammlung ausgewählt, zB:

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 

Dies ist im Grunde, was ich will erreichen . Ist dies nun eher eine Strategie Musterimplementierung oder das Zustandsmuster - obwohl ich den Begriff Strategie verwendet habe, weil es in diesem Fall sowieso mehr passt. Soweit ich das verstehe, ist die Strategie und das Grundmuster grundsätzlich gleich, außer dass ihre Absicht anders ist. Der von Gordon angegebene Link besagt, dass die Absicht eines Zustandsmusters lautet "Einem Objekt erlauben, sein Verhalten zu ändern, wenn sich sein interner Zustand ändert" - aber dies ist hier nicht der Fall. Ich möchte meiner MyCollection-Klasse sagen können, "benutze diesen oder jenen Algorithmus, um mir ein Element zu geben" - nicht "gib mir ein Element, das einen Algorithmus verwendet, den du durch deinen eigenen Zustand bestimmst". Hoffe jemand kann das klären!

Mit freundlichen Grüßen, Daniel

+1

+1, Dieser Smiley wird das Ende von mir sein. xP –

+0

Nun, du hast deine Frage schon selbst beantwortet. Sie möchten keine Staaten verwalten. Sie möchten einzelne Verhaltensweisen austauschen, daher ist Strategie angemessen. Auf einer Nebenbemerkung, da Sie scheinen, deutsch zu sein, kann ich das Buch phpdesignpatterns.de empfehlen – Gordon

Antwort

4

Ihr Ansatz ist richtig. Es ist ein Strategy Pattern (UML diagram):

überprüfen meine Antwort auf diese Frage aus (überspringen, wo es heißt Da Sie dynamisch Verhalten ändern möchten):

Ein Eine Alternative zu Ihrem spezifischen UseCase wäre, die Auswahlstrategien in eine einzelne Serviceklasse zu überführen, anstatt sie Ihrer Collection-Klasse zuzuordnen. Statt die Strategien an die Sammlung zu übergeben, übergeben Sie die Sammlung dann an die Dienstklasse, z.

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

Ich überlasse es Ihnen, ob Sie dies mit statischen Methoden verwenden oder nicht. Um eine Mehrfachinstanziierung von Auswahlstrategien zu verhindern, können Sie die Auswahlobjekte innerhalb des Service zur Wiederverwendung speichern.

Für andere Verhaltensmuster überprüfen

+0

Vielen Dank! Jetzt muss ich nur noch das "Do not .. Need .. Object ... Here .. Urgs .." - Gefühl;) –

+0

ab 5.3, können Sie auch für diesen Ansatz Schließungen verwenden, aber dann Sie Tipp oder Programm kann nicht mehr in ein Interface eingegeben werden. – Gordon

+0

Die Strategie wird verwendet, um einen optimierten Algorithmus zu liefern - wie zum Beispiel die binäre Suche, wenn es sich um ein sortiertes Array handelt, anstatt um eine lineare Suche nach einem unsortierten Array. Außerdem wird die Strategieauswahl oft mit überladenen Methoden durchgeführt, die in PHP nicht unterstützt werden (__call leistet auch hier keine gute Arbeit). Ich denke, dass in diesem Fall das Zustandsmuster geeigneter ist - es wendet die richtige Implementierung basierend auf dem extern konfigurierbaren Zustand an. –

3

Die Lösung, die Sie skizziert ist mehr oder weniger richtig. Sie können das nicht wissen, aber das ist in der Tat "State" Design Pattern. Der Status des Systems wird durch ein Objekt repräsentiert. Wenn Sie den Status ändern müssen, ersetzen Sie einfach das Objekt durch einen anderen Status.

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

würde ich empfehlen, mit dem Aufenthalt, was Sie haben, wie es von GoF bewährt hat tragfähige Lösung zu sein.

+0

Auch danke, ich glaube aber, dass "Strategy Pattern" tatsächlich das richtige Muster und Begriff ist. –

+0

Hey Dan, an diesem Punkt werden wir wahrscheinlich übermäßig pedantisch, aber ich bin ziemlich zuversichtlich, dass Staat genau das ist, wonach du suchst. Der ganze Punkt des Zustands ist, dass das State-Objekt (Implementor) auf das Master-Objekt von außerhalb gesetzt werden kann, während in Strategie die Entscheidung, welche Implementierung zu verwenden ist, innerhalb des Master-Objekts erfolgt, ohne dass es von außen verändert werden kann. Wie auch immer, was du gerade hast, macht Sinn. –

+0

So oder so, das ist immer noch eine Aufwertung wert. Die Gang of Four-Muster sind für mich keine strikten Do-It-This-Way-Mandate, sondern Aufforderungen, die darauf schließen lassen, dass gute Gestaltungsgrundsätze flexibel anzuwenden sind. Es gibt eine Gemeinsamkeit zwischen den staatlichen und strategischen Mustern, da beide umschaltbare Implementierungen als ein Hauptziel haben. Genau zu sagen, welches Muster einen Wert hat, aber die zugrundeliegenden Ideen sind der wahre Gewinn. – Steve314