views:

97

answers:

2

I have a question which is sonewhat more of a design question. I have a class which defines a bunch of functions. However, I want that the behaviour of some functions are changeable during runtime - or at least working like a plugin interface during design time. eg:

class MyClass {  
  public function doSomething();
}

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

I had various Ideas how to implement something like this in "real" code. However, I am not quite sure, which is the right way to go. I first thought I could use an Interface for this purpose.

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!

This approach could be expanded to not have the doSomething provider passed as a parameter, but to set it either as an object member or even class member. However, I did not like that I have to create an instance of n class which does not even contain a single member variable and the only method could easily be a static class function. There's just no need for an object at that point imo. I then was going to try to use function variables - but I'd fall out of OOP then, which I didn't like either. My questions are: which is the best way to implement a system like I described? Which approach would you try or use? Is there anything obvious I might not have thought of? Or should I just go with the interface approach and my bad feeling about instanciating 'bodyless' objects is just nerd preciousness?! I'm curious about your answers!

Edit:

Because I'm really interested in clarifying if this actually is (or should be) a State or a Strategy Pattern, I'll go into some more detail on the implementation. I have a base class of collections from which I derive different specific implementations. I want to be able to select a single element from any specific implementation - however, I want to be able dynamically alter how this element is selected from the collection, eg:

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 

This is basicly what I want to achieve. Is this now more a strategy pattern implementation or the state pattern - albeit I used the term strategy because it fits more in this case anyway. As far as I understand the strategy and state pattern a basicly the same, except their intent is different. The link provided by Gordon states that a state pattern's intent is "Allow an object to alter its behavior when its internal state changes" - but this it not the case here. What I want is to be able to tell my MyCollection class "use this or that algorithm to give me an element" - not "give me an element using an algorithm that you determine through your own state". Hope someone can clarify this!

Best regards, Daniel

+3  A: 

Your approach is correct. It is a Strategy Pattern (UML diagram):

Check out my answer to this question (skip to where it says Since you want to dynamically change behavior):

An alternative to your specific UseCase would be to capsule the Selection Strategies into a single Service class, instead of assigning them to your Collection class. Then instead of passing the strategies to the collection, pass the collection to the Service Class, e.g.

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

I leave it up to you whether to use this with static methods or not. To prevent multiple instantiation of Selection strategies, you could store the Selection objects inside the Service for reuse.

For other Behavioral Patterns check

Gordon
Thanks alot! Now I'll just need to squelch that "Don't.. Need.. Object.. Here.. Urgs.."-feeling ;)
Daniel Baulig
As of 5.3, you could use closures for this approach as well, but then you cannot type hint or program against an interface anymore.
Gordon
Strategy is used to deliver optimised algorithm - like for example Binary Search when dealing with sorted array instead of Linear Search for unsorted array. Also, strategy selection is often implemented using overloaded methods, which are not supported in PHP (__call does not do great job here either). I think State pattern is more appropriate in this case - it applies the right implementation based on externally configurable state.
Michał Rudnicki
Dependency Injection may also be worth looking up, for example at http://martinfowler.com/articles.html - probably overkill for this, but at least worth understanding.
Steve314
@Michał the OP simply wants to exchange single behaviors on the fly. Given from the description, the decision when to use which is not determind from the using object's internal state, but based on external factors. He is not building a state machine. Thus, strategy is appropriate.
Gordon
+2  A: 

The solution you outlined is more or less right. You may not know this, but this is in fact "State" Design Pattern. State of the system is represented by object. If you need to change the state, you just replace the object with another State.

alt text

I would recommend staying with what you have, as it's proven by GoF to be viable solution.

Michał Rudnicki
Also thank you, I do belive though that "Strategy Pattern" actually is the correct pattern and term.
Daniel Baulig
Hey Dan, at this point we are probably getting overly pedantic, but I'm quite confident that State is exactly what you are looking for. The whole point of state is that State object (implementor) can be set on the master object from outside of it, whereas in Strategy the decision on which implementation to use is done within master object without any way of changing it from outside. Anyway, what you have at the moment makes perfect sense.
Michał Rudnicki
Either way, this is still worth an upvote. To me, the Gang of four patterns are not strict do-it-this-way mandates, but prompts that suggest good design principles to be applied flexibly. There is commonality between the state and strategy patterns, since both have switchable implementations as a key goal. Being precise about which pattern has value, but the underlying ideas are the real payoff.
Steve314
I updated my post with some additional information on what I intent to do in my specific case. I'd really like to know if this should be considered a state design pattern or a strategy design pattern. Through my understanding I'd consider this a strategy pattern - but then again I'm not the expert here ;)
Daniel Baulig
Given the controversy whether it is State or Strategy, you will probably have to live with this blurry distinction between these two. I actually think your implementation is clean enough regardless the label you put on it.
Michał Rudnicki