Skip to content

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).

strategy

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.