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 Regist
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
[GoF] Gamma, E., Helm, R., Johnson, R., Vlissides, J., Design Patterns: Elements of Reusable ObjectOriented Software, Reading, Mass.: AddisonWesley, 1995.
[CleanCode] Martin, Robert C. Clean Code: A Handbook of Agile Software Craftsmanship. Upper Saddle River, NJ: Prentice Hall, 2009. Print.