In object-based pro­gram­ming, design patterns support de­vel­op­ers with proven solution ap­proach­es and templates. Once the right solution scheme has been found, only in­di­vid­ual ad­just­ments need to be made. There are currently 70 design patterns in total that are tailored to certain ap­pli­ca­tions. Strategy design patterns focus on the behavior of software.

What is the strategy pattern?

Strategy patterns are among the be­hav­ioral patterns that equip software with different solution methods. These strate­gies include a range of al­go­rithms which are distinct from the actual program and are au­tonomous (i.e. ex­change­able). A strategy design pattern also includes certain spec­i­fi­ca­tions and aids for de­vel­op­ers. 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 behavior can also be realized during software runtime.

How the strategy pattern is presented in UML

Strategy patterns are normally designed with Unified Modelling Language (UML). It vi­su­al­izes design patterns with a stan­dard­ized 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­po­nents is typically used to represent a strategy design pattern:

  • Context
  • Strategy
  • Con­creteS­trat­e­gy

In the strategy design pattern, the basic com­po­nents take on special roles: The be­hav­ioral patterns of the Context class are out­sourced to different Strategy classes. These separate classes contain the al­go­rithms that are referred to as Con­creteS­trate­gies. A reference allows the Context to access the out­sourced cal­cu­la­tion vari­a­tions (Con­creteS­trat­e­gyA, Con­creteS­trat­e­gyB etc.) where necessary. In this process, it does not interact directly with the al­go­rithms but with an interface.

The Strategy interface en­cap­su­lates the cal­cu­la­tion vari­a­tions and can be im­ple­ment­ed by all al­go­rithms si­mul­ta­ne­ous­ly. For in­ter­act­ing with the Context, the generic interface rep­re­sents just one way of trig­ger­ing Con­creteS­trat­e­gy al­go­rithms. 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­go­rithms 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­ces­si­ble that are relevant to the external instance.

We’ll now explain how the design pattern is im­ple­ment­ed 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­i­ga­tion app is to be im­ple­ment­ed 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:

  • Pedes­tri­an (Con­creteS­trat­e­gyA)
  • Car (Con­creteS­trat­e­gyB)
  • Public transport (Con­creteS­trat­e­gyC)

The structure and function of the necessary strategy pattern becomes clear when these spec­i­fi­ca­tions are shown in a UML diagram:

In our example, the client is the graphical user interface (GUI) of a nav­i­ga­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­lat­ed. The Context (navigator class) has the task of cal­cu­lat­ing and pre­sent­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 pedes­tri­an button of the client, the service “Calculate the pedes­tri­an route” (Con­creteS­trat­e­gyA) is requested. The method ex­e­cuteAl­go­rithm() (in our example, the method: cal­cu­lateR­oute (A, B)) accepts a starting point and des­ti­na­tion, and returns a col­lec­tion of route control points. The Context accepts the client command and decides on the right strategy (set­Strat­e­gy: Pedes­tri­an) based on pre­vi­ous­ly 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­Strat­e­gy(). The results of the Con­creteS­trat­e­gy cal­cu­la­tions are used in further pro­cess­ing and the graphical pre­sen­ta­tion of the route in the nav­i­ga­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­creteS­trat­e­gyB) 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­ment­ed with rel­a­tive­ly simple code:

Context:

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

Strategy, Con­creteS­trat­e­gyA, Con­creteS­trat­e­gyB:

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-Verhalten 
        Context context = new Context(); 
        context.execute(); 
        //Verhalten ändern 
        context.setStrategy(new ConcreteStrategyB()); 
        context.execute(); 
    } 
}

What are the ad­van­tages and dis­ad­van­tages of the strategy pattern?

The ad­van­tages of a strategy pattern become clear when you consider the per­spec­tive of a pro­gram­mer and system ad­min­is­tra­tor. In general, the breakdown into au­tonomous 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­lat­ed sections. This allows the size of the navigator class to be reduced by out­sourc­ing strate­gies, and forming sub­class­es is un­nec­es­sary in the Context area.

Since the internal de­pen­den­cies of segments stay within rea­son­able limits in the leaner and neatly seg­re­gat­ed code, changes have fewer im­pli­ca­tions. Sub­se­quent, often time-consuming re­pro­gram­ming is not required as often and may even be elim­i­nat­ed al­to­geth­er. Clearer code segments can also be main­tained better in the long-term, while trou­bleshoot­ing and di­ag­nos­tics 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 behavior (route cal­cu­la­tion) in a variable manner and con­ve­nient­ly choose between different options.

Since the Context of the nav­i­ga­tion app only interacts with an interface due to the en­cap­su­la­tion of the al­go­rithms, it is in­de­pen­dent from the concrete im­ple­men­ta­tion of in­di­vid­ual al­go­rithms. If the al­go­rithms are changed at a later time or new strate­gies 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­creteS­trate­gies for plane and ship routes as well as long-distance transport. The new strate­gies 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­sid­ered to be par­tic­u­lar­ly difficult to develop. Related Context classes could therefore also use the out­sourced strate­gies for route cal­cu­la­tion via an interface and would no longer need to implement these them­selves.

Despite the many ad­van­tages, the strategy pattern also has some dis­ad­van­tages. Due to its more complex structure, software design may result in re­dun­dan­cies and in­ef­fi­cien­cies in internal com­mu­ni­ca­tion. For instance, the generic Strategy interface, which all al­go­rithms need to implement equally, may be oversized in in­di­vid­ual cases.

An example: After the Context has created and ini­tial­ized certain pa­ra­me­ters, it sends them to the generic interface and the method defined there. But the most recently im­ple­ment­ed strategy does not nec­es­sar­i­ly need all com­mu­ni­cat­ed Context pa­ra­me­ters 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­ni­ca­tion with excessive data transfers cannot always be avoided.

In the case of im­ple­men­ta­tion, there is also a close internal de­pen­den­cy between the client and strate­gies. Since the client makes the selection and requests the specific strategy using a trigger command (in our example, to calculate the pedes­tri­an route), it needs to know the Con­creteS­trate­gies. You should therefore only use this design pattern if strategy and behavior changes are important or essential for the use and func­tion­ing of a software program.

These dis­ad­van­tages can be partially cir­cum­vent­ed 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­men­ta­tion in a flyweight pattern. This measure has a positive effect on the ef­fi­cien­cy and memory re­quire­ment of an ap­pli­ca­tion.

Where is the strategy pattern used?

As a fun­da­men­tal design pattern in software de­vel­op­ment, the strategy design pattern is not limited to a certain area of ap­pli­ca­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­abil­i­ty, behavior 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­go­rithms 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 strate­gies. 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­men­ta­tion of gaming software, which has to respond flexibly to changing game sit­u­a­tions during runtime. Different char­ac­ters, special equipment, figure behaviors, or various moves (special movements of a game character) can be stored in the form of Con­creteS­trate­gies.

Another area of ap­pli­ca­tion of strategy patterns is tax software. By ex­chang­ing Con­creteS­trate­gies, 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­pli­ca­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 strate­gies for con­fig­ur­ing com­po­nents in interface de­vel­op­ment. Other ap­pli­ca­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­guish­es itself by the following char­ac­ter­is­tics:

  • Behavior-based (be­hav­ioral patterns and changes are more easily pro­gram­ma­ble and im­ple­mentable; changes are even possible during a program’s runtime)
  • Ef­fi­cien­cy-oriented (out­sourc­ing sim­pli­fies and optimizes code and its main­te­nance)
  • Future-oriented (changes and op­ti­miza­tions are also easy to realize in the medium and long term)
  • Aims at ex­ten­si­bil­i­ty (this is promoted by the modular system and the in­de­pen­dence of objects and classes)
  • Aims at reusabil­i­ty (e.g. multiple use of strate­gies)
  • Aims at optimized controls, usability, and con­fig­ura­bil­i­ty 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­itable software de­vel­op­ment in object-based pro­gram­ming with tailored solutions to problems. Even during the design phase, po­ten­tial­ly upcoming changes and im­prove­ments are optimally prepared. The system is designed for vari­abil­i­ty and dynamism and can generally be con­trolled better. Errors and in­con­sis­ten­cies are resolved more quickly. Thanks to reusable and ex­change­able com­po­nents, de­vel­op­ment costs are saved par­tic­u­lar­ly 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­quent­ly.

Go to Main Menu