Now that we can dynamically change a ball's behavior, let's tackle another problem:
Exercise 2.13
How can we have balls with multiple behaviors but yet not duplicate code for each one of those behaviors?
Let's start with a very straightforward solution:
package ballworld; import java.awt.*; public class DoubleStrategy implements IUpdateStrategy { private IUpdateStrategy _s1 = new CurveStrategy(); private IUpdateStrategy _s2 = new BreathingStrategy(); public void updateState(Ball context) { _s1.updateState(context); _s2.updateState(context); } }
Ta da! No problem. The DoubleStrategy simply holds two strategies and delegates to each of them in turn when asked to updateState. So why stop here?
package ballworld; import java.awt.*; public class TripleStrategy implements IUpdateStrategy { private IUpdateStrategy _s1 = new CurveStrategy(); private IUpdateStrategy _s2 = new BreathingStrategy(); private IUpdateStrategy _s3 = new BreathingStrategy(); public void updateState(Ball context) { _s1.updateState(context); _s2.updateState(context); _s3.updateState(context); }}
We're on a roll now! We could go on and on, making as complex a strategy as we'd like, making a new class for each combination we want. But somewhere around the 439'th combination, we get mightly tired of writing classes. Isn't there an easier way?
Abstraction is the key here. We want to write code that represents that abstraction of multiple, composite strategies. Does what we were doing above depend on the particular concrete strategies that we were using? No? Then we should eliminate the concrete classes, raise the abstraction level and use the abstract superclass (interface) instead. For a combination of two behaviors, we end up with the following:
package ballworld; import java.awt.*; public class MultiStrategy implements IUpdateStrategy { private IUpdateStrategy _s1; private IUpdateStrategy _s2; public MultiStrategy(IUpdateStrategy s1, IUpdateStrategy s2) { _s1 = s1; _s2 = s2; } public void updateState(Ball context) { s1.updateState(context); s2.updateState(context); } }
Notice how we have added a constructor that enables us to initialize the two abstract strategy fields. All we have to do is to construct a MultiStrategy object with the two desired strategies, and we're good to go!
Exercise 2.14
So if we want three behaviors, all we have to do is to make the same sort of thing but with 3 abstract strategy fields, right?
Thus, with just a Multistrategy we are capable of composing arbitrarily complex behaviors!
- 2126 reads