The following code sample is an implementation of the Strategy pattern copied from Wikipedia. My full question follows it...
The Wiki's main
method:
//StrategyExample test application
class StrategyExample {
public static void main(String[] args) {
Context context;
// Three contexts following different strategies
context = new Context(new ConcreteStrategyAdd());
int resultA = context.executeStrategy(3,4);
context = new Context(new ConcreteStrategySubtract());
int resultB = context.executeStrategy(3,4);
context = new Context(new ConcreteStrategyMultiply());
int resultC = context.executeStrategy(3,4);
}
}
The pattern pieces:
// The classes that implement a concrete strategy should implement this
// The context class uses this to call the concrete strategy
interface Strategy {
int execute(int a, int b);
}
// Implements the algorithm using the strategy interface
class ConcreteStrategyAdd implements Strategy {
public int execute(int a, int b) {
System.out.println("Called ConcreteStrategyA's execute()");
return a + b; // Do an addition with a and b
}
}
class ConcreteStrategySubtract implements Strategy {
public int execute(int a, int b) {
System.out.println("Called ConcreteStrategyB's execute()");
return a - b; // Do a subtraction with a and b
}
}
class ConcreteStrategyMultiply implements Strategy {
public int execute(int a, int b) {
System.out.println("Called ConcreteStrategyC's execute()");
return a * b; // Do a multiplication with a and b
}
}
// Configured with a ConcreteStrategy object and maintains a reference to a Strategy object
class Context {
private Strategy strategy;
// Constructor
public Context(Strategy strategy) {
this.strategy = strategy;
}
public int executeStrategy(int a, int b) {
return strategy.execute(a, b);
}
}
Considering specifically the above example, is the Context
class redundant?
For example, I can come up with the following alternate main
implementation by using the existing classes and interface except Context and it will work exactly the same. It is still loosely coupled.
(( Edit: In this simple scenario, when I leave out the Context class, will I be making a future mistake? ))
public static void main(String[] args) {
IStrategy strategy;
// Three strategies
strategy = new ConcreteStrategyAdd();
int resultA = strategy.executeStrategy(3,4);
strategy = new ConcreteStrategySubtract();
int resultB = strategy.executeStrategy(3,4);
strategy = new ConcreteStrategyMultiply();
int resultC = strategy.executeStrategy(3,4);
}
Listing in point form what was discovered through answers and comments:
If any other points would be useful or if this needs correction leave a comment and I'll modify the list accordingly.
Solution. The Strategy pattern suggests that you take a class that does something specific in a lot of different ways and extract all of these algorithms into separate classes called strategies. The original class, called context, must have a field for storing a reference to one of the strategies.
Problem. The strategy pattern is used to solve problems that might (or is foreseen they might) be implemented or solved by different strategies and that possess a clearly defined interface for such cases.
Strategy design pattern is one of the behavioral design pattern. Strategy pattern is used when we have multiple algorithm for a specific task and client decides the actual implementation to be used at runtime.
The strategy pattern has found its place in JDK, and you know what I mean if you have sorted ArrayList in Java. Yes, a combination of the Comparator, Comparable, and the Collections. sort() method is one of the best real-world examples of the Strategy design pattern.
This is the better example of how the real "Context
" class can look in this scenario:
class Accumulator {
private Strategy strategy;
public Accumulator(Strategy strategy) {
this.strategy = strategy;
}
public int accumulate(List<Integer> values) {
int result = values.get(0);
for (int i = 1; i < values.size(); i++) {
result = strategy.execute(result, values.get(i));
}
return result;
}
}
EDIT: Typo in constructor fixed
As the name suggests, the Context
is what encapsulates the point at which the strategy is performed. Without that, you just have a naked Strategy
, and the calling class now picks up an extra responsibility: knowing when to call the Strategy
itself. Your example is perhaps a bit too simple, and in this particular case, I would say that the Context
isn't getting you too much.
An example that perhaps better illustrates the usefulness of a Context
is more like the following:
public class LoadingDock { // Context.
private LoadStrategy ls; // Strategy.
public void setLoadStrategy(LoadStrategy ls) { ... }
// Clients of LoadingDock use this method to do the relevant work, rather
// than taking the responsibility of invoking the Strategy themselves.
public void shipItems(List<ShippingItem> l) {
// verify each item is properly packaged \
// ... | This code is complex and shouldn't be
// verify all addresses are correct | subsumed into consumers of LoadingDock.
// ... | Using a Context here is a win because
// load containers onto available vehicle | now clients don't need to know how a
Vehicle v = VehiclePool.fetch(); // | LoadingDock works or when to use a
ls.load(v, l); // / LoadStrategy.
}
}
Notice how the Strategy
will never be called directly from an external client. Only shipItems
uses the strategy, and the details of the steps it follows are a black box. This allows the Context
to adjust how it uses the strategy without affecting clients. For instance, the steps could be completely reordered or adjusted (or removed entirely) to meet performance objectives or other goals -- but for the client, the external interface of shipItems()
looks exactly the same.
Notice, also, that our example Context
, the LoadingDock
, could change its LoadStrategy
at any time based on its internal state. For example, if the dock is getting too full perhaps it will switch to a more aggressive scheduling mechanism that gets crates off the dock and into trucks faster, sacrificing some efficiency in doing so (maybe the trucks don't get loaded up as efficiently as they could have been).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With