In object-based pro­gram­ming, design patterns support de­velopers with proven solution ap­proaches and templates. Once the right solution scheme has been found, only in­di­vidu­al ad­just­ments need to be made. There are currently 70 design patterns in total that are tailored to certain ap­plic­a­tions. Strategy design patterns focus on the behaviour of software.

What is the strategy pattern?

Strategy patterns are among the be­ha­vi­our­al patterns that equip software with different solution methods. These strategies include a range of al­gorithms which are distinct from the actual program and are autonom­ous (i.e. ex­change­able). A strategy design pattern also includes certain spe­cific­a­tions and aids for de­velopers. For instance, strategy patterns can describe how to assemble classes, arrange a group of classes, and create objects. What’s special about strategy design patterns is that a variable program and object behaviour can also be realised during software runtime.

How the strategy pattern is presented in UML

Strategy patterns are normally designed with Unified Modelling Language (UML). It visu­al­ises design patterns with a stand­ard­ised notation and uses special char­ac­ters and symbols. The UML provides various diagram types for object-based pro­gram­ming. A class diagram with at least three basic com­pon­ents is typically used to represent a strategy design pattern:

  • Context
  • Strategy
  • Con­creteStrategy

In the strategy design pattern, the basic com­pon­ents take on special roles: The be­ha­vi­our­al patterns of the Context class are out­sourced to different Strategy classes. These separate classes contain the al­gorithms that are referred to as Con­creteStrategies. A reference allows the Context to access the out­sourced cal­cu­la­tion vari­ations (Con­creteStrategyA, Con­creteStrategyB etc.) where necessary. In this process, it does not interact directly with the al­gorithms but with an interface.

The Strategy interface en­cap­su­lates the cal­cu­la­tion vari­ations and can be im­ple­men­ted by all al­gorithms sim­ul­tan­eously. For in­ter­act­ing with the Context, the generic interface rep­res­ents just one way of trig­ger­ing Con­creteStrategy al­gorithms. Besides the strategy request, the in­ter­ac­tions with the Context also include data exchange. The Strategy interface is also involved in strategy changes that can take place during a program’s runtime.

Fact

En­cap­su­la­tion prevents direct access to al­gorithms and internal data struc­tures. An external instance (client, Context) can only use cal­cu­la­tions and functions via defined in­ter­faces. Here, only those methods and data elements of an object are ac­cess­ible that are relevant to the external instance.

We’ll now explain how the design pattern is im­ple­men­ted in a practical project using a strategy pattern example.

Ex­plain­ing the strategy pattern with an example

In our example (we are orienting ourselves around the German strategy pattern study project by Philipp Hauer, in which a nav­ig­a­tion app is to be im­ple­men­ted with the help of a strategy design pattern. The app should calculate a route based on normal modes of transport. The user can choose between three options:

  • Ped­es­tri­an (Con­creteStrategyA)
  • Car (Con­creteStrategyB)
  • Public transport (Con­creteStrategyC)

The structure and function of the necessary strategy pattern becomes clear when these spe­cific­a­tions are shown in a UML diagram:

In our example, the client is the graphical user interface (GUI) of a nav­ig­a­tion app with buttons for cal­cu­lat­ing routes. Once the user makes a selection and taps on a button, a concrete route is cal­cu­lated. The Context (navigator class) has the task of cal­cu­lat­ing and present­ing a range of control points on the map. The navigator class has a method for switching the active routing strategy. This means it is possible to switch between modes of transport via the client buttons.

For example, if the user triggers a command with the ped­es­tri­an button of the client, the service ‘Calculate the ped­es­tri­an route’ (Con­creteStrategyA) is requested. The method ex­ecuteAl­gorithm() (in our example, the method: cal­cu­lateRoute (A, B)) accepts a starting point and des­tin­a­tion, and returns a col­lec­tion of route control points. The Context accepts the client command and decides on the right strategy (set­Strategy: Ped­es­tri­an) based on pre­vi­ously described policies. It delegates the request to the strategy object and its interface via a call.

The currently selected strategy is stored in the Context (navigator class) using get­Strategy(). The results of the Con­creteStrategy cal­cu­la­tions are used in further pro­cessing and the graphical present­a­tion of the route in the nav­ig­a­tion app. If the user opts for a different route by clicking on the ‘Car’ button af­ter­wards, for example, the Context switches to the requested strategy (Con­creteStrategyB) and initiates a new cal­cu­la­tion by means of another call. At the end of the process, a modified route de­scrip­tion is provided for travel by car.

In our example, the pattern mechanism can be im­ple­men­ted with re­l­at­ively simple code:

Context:

public class Context {
    //prescribed standard value (default behaviour): ConcreteStrategyA
    private Strategy strategy = new ConcreteStrategyA(); 
    public void execute() { 
        //delegates the behaviour to a Strategy object
        strategy.executeAlgorithm(); 
    }
    public void setStrategy(Strategy strategy) {
        strategy = strategy;
    }
    public Strategy getStrategy() { 
        return strategy; 
    } 
}

Strategy, Con­creteStrategyA, Con­creteStrategyB:

interface Strategy { 
    public void executeAlgorithm(); 
} 
class ConcreteStrategyA implements Strategy { 
    public void executeAlgorithm() { 
        System.out.println("Concrete Strategy A"); 
    } 
} 
class ConcreteStrategyB implements Strategy { 
    public void executeAlgorithm() { 
        System.out.println("Concrete Strategy B"); 
    } 
}

Client:

public class Client {
public static void main(String[] args) {
//default behaviour
Context context = new Context();
context.execute();
//change behaviour
context.setStrategy(new ConcreteStrategyB());
context.execute();
}
}

What are the ad­vant­ages and dis­ad­vant­ages of the strategy pattern?

The ad­vant­ages of a strategy pattern become clear when you consider the per­spect­ive of a pro­gram­mer and system ad­min­is­trat­or. In general, the breakdown into autonom­ous modules and classes leads to an improved structure of the program code. Pro­gram­mers of our example app work with more stream­lined code segments in the en­cap­su­lated sections. This allows the size of the navigator class to be reduced by out­sourcing strategies, and forming sub­classes is un­ne­ces­sary in the Context area.

Since the internal de­pend­en­cies of segments stay within reas­on­able limits in the leaner and neatly se­greg­ated code, changes have fewer im­plic­a­tions. Sub­sequent, often time-consuming re­pro­gram­ming is not required as often and may even be elim­in­ated al­to­geth­er. Clearer code segments can also be main­tained better in the long-term, while troubleshoot­ing and dia­gnostics are sim­pli­fied.

The controls benefit too since the example app can be equipped with a user-friendly interface. The buttons allow users to easily control the program behaviour (route cal­cu­la­tion) in a variable manner and con­veni­ently choose between different options.

Since the Context of the nav­ig­a­tion app only interacts with an interface due to the en­cap­su­la­tion of the al­gorithms, it is in­de­pend­ent from the concrete im­ple­ment­a­tion of in­di­vidu­al al­gorithms. If the al­gorithms are changed at a later time or new strategies in­tro­duced, the Context code does not need to be changed. So, route cal­cu­la­tion could be quickly and easily expanded with ad­di­tion­al Con­creteStrategies for plane and ship routes as well as long-distance transport. The new strategies only need to correctly implement the Strategy interface.

Strategy patterns simplify what is generally the difficult task of pro­gram­ming object-based software, thanks to another advantage. They enable the design of reusable software (modules), which are con­sidered to be par­tic­u­larly difficult to develop. Related Context classes could therefore also use the out­sourced strategies for route cal­cu­la­tion via an interface and would no longer need to implement these them­selves.

Despite the many ad­vant­ages, the strategy pattern also has some dis­ad­vant­ages. Due to its more complex structure, software design may result in re­dund­an­cies and in­ef­fi­cien­cies in internal com­mu­nic­a­tion. For instance, the generic Strategy interface, which all al­gorithms need to implement equally, may be oversized in in­di­vidu­al cases.

An example: After the Context has created and ini­tial­ised certain para­met­ers, it sends them to the generic interface and the method defined there. But the most recently im­ple­men­ted strategy does not ne­ces­sar­ily need all com­mu­nic­ated Context para­met­ers and therefore does not process them all. In other words, a provided interface is not always optimally used in the strategy pattern and increased com­mu­nic­a­tion with excessive data transfers cannot always be avoided.

In the case of im­ple­ment­a­tion, there is also a close internal de­pend­ency between the client and strategies. Since the client makes the selection and requests the specific strategy using a trigger command (in our example, to calculate the ped­es­tri­an route), it needs to know the Con­creteStrategies. You should therefore only use this design pattern if strategy and behaviour changes are important or essential for the use and func­tion­ing of a software program.

These dis­ad­vant­ages can be partially cir­cum­ven­ted or offset. For example, the number of object instances that can accrue in a large number in the strategy pattern can often be reduced by an im­ple­ment­a­tion in a flyweight pattern. This measure has a positive effect on the ef­fi­ciency and memory re­quire­ment of an ap­plic­a­tion.

Where is the strategy pattern used?

As a fun­da­ment­al design pattern in software de­vel­op­ment, the strategy design pattern is not limited to a certain area of ap­plic­a­tion. Instead, the type of problem is highly relevant for the use of the design pattern. Any software that needs to solve pending tasks and problems with vari­ab­il­ity, behaviour options, and changes is a prime candidate for the design pattern.

For instance, programs that offer different storage formats for files or various sort and search functions can use strategy design patterns. Likewise in data com­pres­sion, programs are used that implement different com­pres­sion al­gorithms based on the design pattern. This way, they can variably convert videos into a desired space-saving file format or restore com­pressed archive files (e.g. ZIP or RAR files) into their original state using special unpacking strategies. Another example is saving a document or an image in different file formats.

What’s more, the design pattern is involved in the de­vel­op­ment and im­ple­ment­a­tion of gaming software, which has to respond flexibly to changing game situ­ations during runtime. Different char­ac­ters, special equipment, figure be­ha­viours, or various moves (special movements of a game character) can be stored in the form of Con­creteStrategies.

Another area of ap­plic­a­tion of strategy patterns is tax software. By ex­chan­ging Con­creteStrategies, rates can easily be adjusted for pro­fes­sion­al groups, countries, and regions. Moreover, programs that convert data into different graphic formats (e.g. as line, circle, or bar charts) use strategy patterns.

More specific ap­plic­a­tions of strategy patterns can be found in the Java standard library (Java API) and in Java GUI toolkits (e.g. AWT, Swing, and SWT), which use a layout manager in the de­vel­op­ment and gen­er­a­tion of graphical user in­ter­faces. This is able to implement different strategies for con­fig­ur­ing com­pon­ents in interface de­vel­op­ment. Other ap­plic­a­tions of strategy design patterns include database systems, device drivers, and server programs.

Key prop­er­ties of the strategy pattern at a glance

Within the extensive range of design patterns, the strategy design pattern dis­tin­guishes itself by the following char­ac­ter­ist­ics:

  • Behaviour-based (be­ha­vi­our­al patterns and changes are more easily pro­gram­mable and im­ple­ment­able; changes are even possible during a program’s runtime)
  • Ef­fi­ciency-oriented (out­sourcing sim­pli­fies and optimises code and its main­ten­ance)
  • Future-oriented (changes and op­tim­isa­tions are also easy to realise in the medium and long term)
  • Aims at ex­tens­ib­il­ity (this is promoted by the modular system and the in­de­pend­ence of objects and classes)
  • Aims at re­usab­il­ity (e.g. multiple use of strategies)
  • Aims at optimised controls, usability, and con­fig­ur­ab­il­ity of software
  • Requires extensive con­cep­tu­al con­sid­er­a­tions in advance (what can be out­sourced to which strategy classes at which points and how?)
Summary

Strategy patterns enable efficient and prof­it­able software de­vel­op­ment in object-based pro­gram­ming with tailored solutions to problems. Even during the design phase, po­ten­tially upcoming changes and im­prove­ments are optimally prepared. The system is designed for vari­ab­il­ity and dynamism and can generally be con­trolled better. Errors and in­con­sist­en­cies are resolved more quickly. Thanks to reusable and ex­change­able com­pon­ents, de­vel­op­ment costs are saved par­tic­u­larly in complex projects with a long-term time horizon. However, it’s important to find the right balance. Design patterns are often used either too sparingly or too fre­quently.

Go to Main Menu