Strategy¶
A strategy is a design pattern that gives you different ways to do the same activity. This activity we want to execute at runtime. We have different ways to do a certain operation. So we have a group of algorithms that we want to use interchangeably (e.g. depending on the application configuration or the user's choice).
Construction¶
In order to use the Strategy
pattern, we need to define the following:
- a common interface for a group of algorithms performing a certain operation
- several implementations of the above interface, i.e. a group of common algorithms
- a class that will choose an appropriate strategy based on the context (i.e. configuration or other external factors).
Example¶
The example consists of the following parts:
- an interface
SpacesModificationStrategy
, which is the common part of the group of algorithms that allows to modify spaces in the input text. - three implementations of the
SpacesModificationStrategy
interface:DoubleSpacesStrategy
- replaces each space with two.RemoveSpacesStrategy
- removes every space.ReplaceWithUnderscoreStrategy
- replaces every space with a_
character.
SpacesModificationStrategyProvider
- allows you to choose the right strategy based on the type.StrategyUsage
- shows an example of using the pattern.
public interface SpacesModificationStrategy {
String modify(String input);
}
public class DoubleSpacesStrategy implements SpacesModificationStrategy {
@Override
public String modify(final String input) {
final StringBuilder stringBuilder = new StringBuilder(input.length());
for (final char c : input.toCharArray()) {
if (c != ' ') {
stringBuilder.append(c);
} else {
stringBuilder.append(" ");
}
}
return stringBuilder.toString();
}
}
public class RemoveSpacesStrategy implements SpacesModificationStrategy {
@Override
public String modify(final String input) {
final StringBuilder stringBuilder = new StringBuilder(input.length());
for (final char c : input.toCharArray()) {
if (c != ' ') {
stringBuilder.append(c);
}
}
return stringBuilder.toString();
}
}
public class ReplaceWithUnderscoreStrategy implements SpacesModificationStrategy {
@Override
public String modify(final String input) {
final StringBuilder stringBuilder = new StringBuilder(input.length());
for (final char c : input.toCharArray()) {
if (c != ' ') {
stringBuilder.append(c);
} else {
stringBuilder.append('_');
}
}
return stringBuilder.toString();
}
}
public enum StrategyType {
DOUBLE,
REMOVE,
REPLACE
}
public class SpacesModificationStrategyProvider {
public SpacesModificationStrategy get(final StrategyType strategyType) {
switch (strategyType) {
case DOUBLE:
return new DoubleSpacesStrategy();
case REMOVE:
return new RemoveSpacesStrategy();
case REPLACE:
return new ReplaceWithUnderscoreStrategy();
}
throw new UnsupportedOperationException("Unsupported strategy type");
}
}
public class StrategyUsage {
public static void main(String[] args) {
final StrategyType strategyType = StrategyType.valueOf(args[0]);
final String input = "hello from SDA knowledge base!";
final SpacesModificationStrategyProvider provider = new SpacesModificationStrategyProvider();
SpacesModificationStrategy strategy = provider.get(strategyType);
final String output = strategy.modify(input);
System.out.println("Result is " + output);
}
}
In conclusion, the strategy is worth using if you prefer composition over inheritance. Besides, the common interface hides some implementation details, but the end-user must be aware that there are different strategies with some differences.