wood burning stoves 2.0*
The moose likes OO, Patterns, UML and Refactoring and the fly likes Strategy Pattern Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login
JavaRanch » Java Forums » Engineering » OO, Patterns, UML and Refactoring
Bookmark "Strategy Pattern" Watch "Strategy Pattern" New topic
Author

Strategy Pattern

Mr. C Lamont Gilbert
Ranch Hand

Joined: Oct 05, 2001
Posts: 1170

I read a few places where people say if you have an if-else construct or a switch statement you can replace it with the strategy pattern. And that the strategy pattern is the correct way to handle if-else chains.

I do have such a chain, but I do not see how the strategy pattern will help me. It seems to simply shift the if-elseing up the chain a bit such that instead of if-elseing within a certain class you start if-elseing at the creation of that class. Ultimately it seems like the if-else only moves but is not destroyed!?

Is that your experience?
Stan James
(instanceof Sidekick)
Ranch Hand

Joined: Jan 29, 2003
Posts: 8791
The trick is to move the code that used to be inside each choice in the if-else block into its own class. Strategy classes can wind up being quite short but they can also be as complex as anything else. For example, say we work with a number of appliances:

If I put "toaster stuff" and "blender stuff" and "refrigerator stuff" into three classes I can do something like:

The if-else structure is gone, the code for toaster, blender and refrigerator is separated into three little classes that each do exactly one thing well. This is all good.

Further, I can add new products without touching this method. I can add a mapping from product type to strategy class in configuration and add them without touching the factory, either. Every class I don't modify is one I won't break.

And there's more! Because this routine and the factory don't directly reference RefrigeratorStrategy, there is no compile time dependency. The Stove team can introduce a whole new Stove component to the system without asking the Core team for a new jar.

Finally, if the if-else chain were long enough, the strategy stuff might give us some performance gain. The time to get a strategy from the factory using configuration is about constant while the time to go through the if-else logic grows with every new product.

I'm a strategy fan. Can you tell?


A good question is never answered. It is not a bolt to be tightened into place but a seed to be planted and to bear more seed toward the hope of greening the landscape of the idea. John Ciardi
Ilja Preuss
author
Sheriff

Joined: Jul 11, 2001
Posts: 14112
I'm a Strategy fan, too. On the other hand, not every if-else-chain or switch statement *should* be replaced by Strategy - that would simply be overkill.

I see mainly three motivations for introducing a Strategy in this case:

- The switch is duplicated at other places - whenever I introduce another case, I have to remember to introduce it at the other places, too. (Single Choice Principle)

- The cases that need to be handled change at a different rate than the rest of the class - most often it's less stable. (Single Responsibility Principle)

- Introducing the Strategy considerably simplifies unit testing.

Can you show us your code?


The soul is dyed the color of its thoughts. Think only on those things that are in line with your principles and can bear the light of day. The content of your character is your choice. Day by day, what you do is who you become. Your integrity is your destiny - it is the light that guides your way. - Heraclitus
Roger Chung-Wee
Ranch Hand

Joined: Sep 29, 2002
Posts: 1683
Looking at this line of code:


Would the getStrategyFor method look like this:


Or is there a more elegant way to code it?


SCJP 1.4, SCWCD 1.3, SCBCD 1.3
Ilja Preuss
author
Sheriff

Joined: Jul 11, 2001
Posts: 14112
I typically prefer

return (Strategy) strategiesByProductTypeMap.get(productType);

strategiesByProductTypeMap is a HashMap which in the simplest case is populated hardcoded inside the factory. In other cases, the strategies might be registered from the outside (in which case it's more of a Registry than a Factory) - some kind of plugin mechanism.
Stan James
(instanceof Sidekick)
Ranch Hand

Joined: Jan 29, 2003
Posts: 8791
My factories usually have one of a couple internal strategies of their own.

Either way, it's neat to load the map from configuration. The factory might have methods like:

I like an "application assembler" to read configuration and pump the strategy info into the factory. This makes it easy to plug in special strategies for testing. I got the "assembler" name from Robert Martin's paper on Inversion of Control tho I was already doing it.
Mr. C Lamont Gilbert
Ranch Hand

Joined: Oct 05, 2001
Posts: 1170

But this is just putting the if-else inside of the Map. And then the map will probably be static somewhere.

I checked my program and this chaining is occuring in my factories. Its sorta like so;

1st factory type:
pass model into factory
factory does if-else chain and creates view based on model type.

2nd factory type:
pass model into factory
factory does if-else chain and creates a live db connected model based on the model passed in. Sort of like a prototype.

So I guess I can creata a set of creation strategies and put them in a map, then when a particular model is passed in, get the strategy out of the map and execute it?

I didn't realize map was such an intimate part of the strategy pattern
[ October 10, 2005: Message edited by: Mr. C Lamont Gilbert ]
Stan James
(instanceof Sidekick)
Ranch Hand

Joined: Jan 29, 2003
Posts: 8791
By using hashes or other advanced lookups maps are a big improvement over regular if-then-else. And even if there was no advantage there, the control over dependencies and the stability of the map lookup over an ever changing if-then-else block often make strategy worth while.

And strategy doesn't have to be that dynamic. I get stuck repeating the way I use them most often, but there are many other choices. You can have strategies that change based on any criteria. Imagine a timer that goes off at 7:00 PM and says

someService.setStrategy( new EveningStrategy() );

with no factory at all. Note that the service might execute the current strategy millions of times with zero if-else tests or map lookups.

The RoboCode robot game has autonomous killer tanks; some of the tank implementations shift strategies based on how near opponents are or how many are left. They might have a short set of if-else choices to set an aiming strategy only when conditions change rather than every time they want to shoot. Again, one strategy shift to any number of passes through the strategy.

Command pattern is very close in structure. If the client passes the strategy into the service it's more of a command. The service might execute the strategy on another machine or shifted later in time:

someService.doOperation( new EveningCommand( theData ) );

TimerTask is a Command by this definition.

It's all good clean fun, no?
[ October 10, 2005: Message edited by: Stan James ]
Ilja Preuss
author
Sheriff

Joined: Jul 11, 2001
Posts: 14112
Originally posted by Mr. C Lamont Gilbert:
But this is just putting the if-else inside of the Map.


If the Map is hardcoded, yes. It can be filled in many of ways, though. And even if it is hardcoded, you still have different kinds of compile time dependencies in your code.

And then the map will probably be static somewhere.


Not so probable when I have any influence on the design...

I didn't realize map was such an intimate part of the strategy pattern


It isn't. It's just one possible way of deciding which strategy to use.

Another way that is quite common is that some code is called from different places, and every such place has hardcoded which Strategy to use. Sometimes they are even implemented as anonymous inner classes.
Mr. C Lamont Gilbert
Ranch Hand

Joined: Oct 05, 2001
Posts: 1170

Since I last posted I implemented the technique with the map ignoring my doubts. It kind of turned into the command mentioned above since my strategy requires data to do its thing, and the data type varies with the strategy.

It has turned out just as you two have stated and that I do in fact not have any static map, and also in some places I am sorta invoking a strategy that is predetermined. its still dynamic to me, but the JVM can tell its really not.

My concern at this point is that I have to load all the strategies when the 'strategy factory' is created; then i stuff them into the map. If I try lazy loading I end up with an if-else.

All in all, its nice. Im losing some type safety, but I seem to be gaining in extensibility.
Ryan McGuire
Ranch Hand

Joined: Feb 18, 2005
Posts: 1006
    
    3
Originally posted by Mr. C Lamont Gilbert:

My concern at this point is that I have to load all the strategies when the 'strategy factory' is created; then i stuff them into the map. If I try lazy loading I end up with an if-else.


Even that isn't horrible. At least you have a single if-else in your factory instead of a bunch them distributed throughout your code.
Ilja Preuss
author
Sheriff

Joined: Jul 11, 2001
Posts: 14112
Originally posted by Mr. C Lamont Gilbert:

All in all, its nice. Im losing some type safety, but I seem to be gaining in extensibility.


 
jQuery in Action, 2nd edition
 
subject: Strategy Pattern
 
Similar Threads
Stratergy Design Pattern?
Anyone got an elegant solution?
struts
Is DRY enough to justify inheritance?
which pattern to use for message conv. and trf.