17

I can't seem to get my head around what advantages the strategy pattern offer. See the example below.

//Implementation without the strategy pattern
class Registry {

    public function Func1(){
         echo 'called function 1';
    }

    public function Func2(){
         echo 'called function 2';
    }

}

$client = new Registry();
$client->Func1();
$client->Func2();

//Implementation with strategy pattern
interface registry {
     public function printMsg();
}

class Func1 implements registry {
     public function printMsg(){
         echo 'called function 1';
    }
}

class Func2 implements registry {
     public function printMsg(){
         echo 'called function 2';
    }
}

class context {

      public function printMsg(Registry $class){
          $class->printMsg();
      }
}

$client = new context();
$client->printMsg(new Func1());
$client->printMsg(new Func2());

In the above two example what advantages will the strategy pattern will offer and how is it better then the first approach? Why should I use strategy pattern?

The above example code might contain errors please ignore the code.

arogachev
  • 33,150
  • 7
  • 114
  • 117
Jay Bhatt
  • 5,601
  • 5
  • 40
  • 62

6 Answers6

35

The intent of the Strategy pattern is to:

Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it. [GoF:349]

To understand what this means, you have to (emphasis mine)

Consider what should be variable in your design. This approach is the opposite of focusing on the cause of redesign. Instead of considering what might force a change to a design, consider what you want to be able to change without redesign. The focus here is on encapsulating the concept that varies, a theme of many design patterns. [GoF:29]

In other words, strategies are related pieces of code you can plug into a client (another object) at runtime to change its behavior. One reason to do this, is to prevent you from having to touch the client each time a new behavior is added (cf. Open Closed Principle (OCP) and Protected Variation). In addition, when you got sufficiently complex algorithms, putting them into their own classes, helps adhering to the Single Responsibility Principle (SRP).

I find the example in your question somewhat ill-suited to grasp the usefulness of the Strategy Pattern. A Registry should not have a printMsg() method and I cannot make much sense of the example as such. An easier example would be the example I give in Can I include code into a PHP class? (the part where I talk about Strategy begins about halfway down the answer).

But anyway, in your first code, the Registry implements the methods Func1 and Func2. Since we assume these to be related algorithms, let's pretend they really are persistToDatabase() and persistToCsv() to have something to wrap our mind around. Let's also imagine that, at the end of an application request, you call one of these methods from a shutdown handler (the client).

But which method? Well, that depends on what you configured and the flag for that is obviously stored in the Registry itself. So in your client you end up with something like

switch ($registry->persistStrategy)
{
    case 'database':
        $registry->persistToDatabase();
    case 'csv':
        $registry->persistToCsv();
    default:
        // do not persist the database
}

But switch statements like this are bad (cf. CleanCode:37ff). Imagine your customer requests you to add a persistToXml() method. Not only do you have to change your Registry class now to add another method, but you also have to change the client to accommodate for that new feature. That's two classes you have to change, when OCP tell us that our classes should be closed for modification.

One way to improve that would be to add a generic persist() method on the Registry and move the switch/case into it so the client only needs to call

$registry->persist();

That's better but it still leaves us with the switch/case and it still forces us to modify the Registry each time we add a new way to persist it.

Now also imagine your product is a framework used by many developers and they come up with their own persist algorithms. How can they add them? They'd have to extend your class but then they'd also have to replace all the occurrences in the framework where yours was used. Or they just write them into your class, but then they'd have to patch the class each time you provided a new version of it. So that's all a can of worms.

Strategy to the rescue. Since the persist algorithms are the stuff that varies, we will encapsulate them. Since you already know how to define a family of algorithms, I'll skip that part and only show the resulting client:

class Registry
{
    public function persist()
    {
        $this->persistable->persist($this->data);
    }
    public function setPersistable(Persistable $persistable)
    {
        $this->persistable = $persistable
    }
    // other code …

Nice, we refactored the conditional with polymorphism. Now you and all the other developers can set whatever Persistable as the desired Strategy:

$registry->setPersistable(new PersistToCloudStorage);

And that's it. No more switch/case. No more Registry hacking. Just create a new class and set it. The Strategy lets the algorithm vary independently from clients that use it.

Also see

End Notes:

[GoF] Gamma, E., Helm, R., Johnson, R., Vlissides, J., Design Patterns: Elements of Reusable Object­Oriented Software, Reading, Mass.: Addison­Wesley, 1995.

[CleanCode] Martin, Robert C. Clean Code: A Handbook of Agile Software Craftsmanship. Upper Saddle River, NJ: Prentice Hall, 2009. Print.

Community
  • 1
  • 1
Gordon
  • 312,688
  • 75
  • 539
  • 559
9

Basically Strategy is for grouping functionality across multiple classes. Oh and you have a typo in your code

class context {

      public function printMsg(registry $class){
          $class->printMsg();
      }
}

With the name of the interface of the type hinting. To be more clear let me show you a small example. Imagine you have an Iphone and an Android What's their common point? One is They are both phones. you could create an interface like this

interface Telephone {
    public function enterNumber();
    public function call();
    public function sentTextMessage();
}

and implement the interface in each of your telephones:

class Iphone implements Telephone {
     // implement interface methods
}

class Android implement Telephone {

}

and you can have a service attached to all phones like a GPS on you car: and be sure that if you plug a telephone (usually with the interface BlueTooth). you could do something like this:

class carGps{
   public function handFreeCall(Telephone $tel){
       $this->amplifyVolume($tel->call());
   }
}


$tomtom = new CarGps();
$tomtom->handFreeCall(new Iphone());
//or if you like to:
$tomtom->handFreeCall(new Android());

as an application developer, you'll be able to use your handFreeCall on every phones that implements the Telephone interface without breaking your code, because you'll know that the telephone is capable of calling.

Hope I helped.

TCB13
  • 3,067
  • 2
  • 39
  • 68
Abdoul Sy
  • 580
  • 3
  • 10
  • Thanks the example explains the strategy pattern well. Can you point our the potential problems I will face in the first example code by not using the pattern? And I can do grouping and code organization without the pattern using abstract classes and interfaces. I.e. Some variations of this pattern combine interface with an abstract context class what benefits they get by doing this? – Jay Bhatt Jul 18 '13 at 11:55
  • 2
    That's a bit hard to get our heads around strategy before we use it. It really depends on how scalabe you want your code to be. what's good with strategy is that you can group different class by a common point. It works well when we want to loop over objects. imagine you have a feed with your facebook or linkedin messages etc... on the same page. if you want to treat each the same way (show message, comment), with the example above replacing you could do something like: foreach($feedItems as $i){ printMessage($i); } instead of looping over all facebook, then twitter etc. ... – Abdoul Sy Jul 18 '13 at 12:07
  • Basically the problem that you'll be facing will show up if you have to create a class that behaves nearly exactly like yours but not exactly, without strategy it would be much harder to write code, to understand or to test it. the combinaison of inheritance and Strategy, is mostly to share properties or method across those classes, where the code is extremely close – Abdoul Sy Jul 18 '13 at 12:09
  • I see your point basically grouping classes using strategy will give me the ability to access them effectively. – Jay Bhatt Jul 18 '13 at 12:17
  • Not just that Human related example is a Door door is an interface whatever the kind of door it will let you through and be able to be locked and have dimensions, if you want a door at your place you'll want to do that pass through and make it fit. YOU are the door manufacturer, when you write an interface; and you want all the other manufacturers to: when they build a door, will create a door that you'll be able to use in your kingdom. It's effective when you code as a team of manufacturers, so that they don't screw up your code by writing shitty doors that explode when pressing the handle. – Abdoul Sy Jul 18 '13 at 12:21
2

Strategy pattern helps in abstracting out the algorithmic approach for doing some particular work. Suppose you want to sort an array of numbers via different algorithms, In this case it is better to apply a strategy pattern than to tightly couple the algorithm with your code.

In the class where you are instantiating the class composed of strategy you instantiate the strategy class reference by passing it to the constructor of composed class.

This way you we are programming to an interface and not implementation and therefore at any point of time we can add more classes to the hierarchy of strategy class and pass the same to the class composed of strategy

Kindly go through the following link

Sanyam Goel
  • 2,138
  • 22
  • 40
  • I agree that strategy pattern will help abstracting the algorithms and adding and updating of algorithms will become easier. But in the first example if I want to add a new algorithm I just add a new function and update the client ($client->newAlgo();) and same thing for update. In the strategy approach I can add another class containing the algorithm and update the client. In both of the approaches I am doing same thing at different places. So what advantage I gain by abstracting? and how do they differ as I am doing same thing at two places? – Jay Bhatt Jul 18 '13 at 11:32
  • @JayBhatt, No thats what you need to understand with strategy pattern I do not require any change in the source code of the class composed of strategy, but in the example you showed you will have to make changes to the source code . I would agree that the patterns are helpful in compiled languages where what you deliver to client is the object code and not source code . Here the intention is to have such a flexibility in code that changes do not effect the much of the part of the remaining system. But for languages interpreted from source code Why to have such a flexibly is a question really – Sanyam Goel Jul 18 '13 at 11:39
  • Your speaking in general terms and lets not worry about delivering this to any client yet. I would appreciate if you can give examples related to the example code. I.e. the potential problems (now and in future) of not using the strategy pattern in the example code and improvements. One clear advantage of using of using the approach without the pattern is that its easy to implement and understand any new person working on it should be able to pick this up quickly rather then understanding various classes and interfaces. – Jay Bhatt Jul 18 '13 at 11:45
  • Also to provide benefits like abstraction code organization I can use inheritance, interface and abstract classes. The point I am trying to make here is that the design patterns are created to solve problems, so I am really after the problem which the strategy pattern solves. – Jay Bhatt Jul 18 '13 at 11:47
  • I wont be posting code from projects that is for sure. But I dont know how may CR's you have faced from customer where you had to make a change to a lot of source code. We had because of bad designs. With proper patterns applied you have to do changes at minimum places. Any ways I will drop you link of swt GUI having custom widgets where a lot of patterns have been used and on each CR raised we don't have to change much I can assure you – Sanyam Goel Jul 18 '13 at 11:51
  • I don't want code I can code for myself. And the thing that you are mentioning is exactly what I am after. You are saying that you had to change lots of files and by using this pattern you reduced the number of files which needed to be changed. But how? In the above example if I want to change something I will have to change the same number of files regardless of the approach. – Jay Bhatt Jul 18 '13 at 11:57
  • Ok let me try and explain . Here you have different strategies in different files func1 func2 etc and your "class context" has a reference to an interface not the implementation . Now when you instantiate context in a new file you can choose any strategy that you would like your context to have. Please follow the next comment.. – Sanyam Goel Jul 18 '13 at 12:06
  • But had you kept strategy logic in the context file itself you would have required to change the context file ( your program should be closed for modification) and here if you apply strategy you never need to make changes to context class. when you add new class (strategy say func3) you are adding to the already existing system (extension). by change of only one statement i.e, in instantiation of context you have to pass the new strategy you get desired result and this way you follow open closed principle(your program should be open for extension, but closed for modification). – Sanyam Goel Jul 18 '13 at 12:06
  • I see the point your trying to make. But if its not much trouble would be able to please give an example with code for this? Just use A, B whatever you like doesn't have to be a real code or anything online. – Jay Bhatt Jul 18 '13 at 12:18
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/33699/discussion-between-sanyam-goel-and-jay-bhatt) – Sanyam Goel Jul 18 '13 at 12:22
0

There's a library and a symfony bundle here:

https://packagist.org/packages/pugx/godfather

liuggio
  • 384
  • 3
  • 3
0

A couple of important features of the Strategy pattern as envisioned by the Gang of Four are:

  1. It has no conditional statements (so leave out your switch, if, etc conditionals)
  2. It has a Context participant

Most of the advice on the Strategy leaves out the Context participant. You can find five different examples of the Strategy pattern in PHP here: http://www.php5dp.com/category/design-patterns/strategy/

Bill
  • 95
  • 3
0

Often, examples are somewhat odd, describing ducks, cats or else. Here is an example of Strategy Pattern used in displaying alerts. (Extending the Gordon's answer).

1. Interface for the methods, that vary (i.e., alert format in the case):

require_once("initialize/initialize.php");
interface alert{
public function alert($message);
};

2. Methods, that implement alert interface.

class alertBorderBullet implements alert{
public function alert($message){
$alert = "<p style='border:1px solid #eee; padding:4px; padding-left:8px; padding-right:8px; border-left:4px solid #FC0; margin-top:8px; margin-bottom:8px; color:#888'>".$message."</p>";
return $alert;
}
};

class alertOrangeBgnd implements alert{
public function alert($message){
$alert = "<p style='color:#fff; background-color:#ff9c3a; padding:4px; padding-left:8px; padding-right:8px; margin-top:8px; margin-bottom:8px; border-left:4px solid #e471bd;'>".$message."</p>";
return $alert;
}
};

class alertRed implements alert{
public function alert($message){
$alert = "<p style='color:#c11; background-color:#efefef; padding:4px; padding-left:12px; padding-right:8px; margin-top:8px; margin-bottom:8px;'>".$message."</p>";
return $alert;
}
};

3. Messenger, to separate alert method setting and getting from other objects in a project.

class alertMessenger{
protected $_alert;
public function setAlertType(alert $alert){$this->_alert = $alert;}
public function returnAlert($message){return $this->_alert->alert($message);}
};

4. A random project object that will use "alerting" in different ways.

class randomObject{
public $alert;
public function __construct(){
    $this->alert = new alertMessenger;
}
// More code here...
};

$randomObject = new randomObject;
$randomObject->alert->setAlertType(new alertRed);
echo $randomObject->alert->returnAlert($message="Red text for critical info");
$randomObject->alert->setAlertType(new alertBorderBullet);
echo $randomObject->alert->returnAlert($message="Border, bullet and pale-color text");
echo $randomObject->alert->returnAlert($message="Repeat, check style permanence");
$randomObject->alert->setAlertType(new alertOrangeBgnd);
echo $randomObject->alert->returnAlert($message="Again, another redefined message style");

randomObject, when initialized (for this case) automatically creates an instance of alertMessanger and makes its methods available. Behaviors can be set and messages echoed. Other alert formats can be created and used when necessary by setAlertType and then returnAlert.

bbe
  • 344
  • 3
  • 16