Skip to main content
Engineering LibreTexts

10.5: Factor out Strategy

  • Page ID
    32425
  • \( \newcommand{\vecs}[1]{\overset { \scriptstyle \rightharpoonup} {\mathbf{#1}} } \) \( \newcommand{\vecd}[1]{\overset{-\!-\!\rightharpoonup}{\vphantom{a}\smash {#1}}} \)\(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\) \(\newcommand{\id}{\mathrm{id}}\) \( \newcommand{\Span}{\mathrm{span}}\) \( \newcommand{\kernel}{\mathrm{null}\,}\) \( \newcommand{\range}{\mathrm{range}\,}\) \( \newcommand{\RealPart}{\mathrm{Re}}\) \( \newcommand{\ImaginaryPart}{\mathrm{Im}}\) \( \newcommand{\Argument}{\mathrm{Arg}}\) \( \newcommand{\norm}[1]{\| #1 \|}\) \( \newcommand{\inner}[2]{\langle #1, #2 \rangle}\) \( \newcommand{\Span}{\mathrm{span}}\)\(\newcommand{\AA}{\unicode[.8,0]{x212B}}\)

    Intent Eliminate conditional code that selects a suitable algorithm by applying the Strategy design pattern.

    Problem

    How do you make a class whose behavior depends on testing the value of some variable more extensible?

    This problem is difficult because:

    • New functionality cannot be added without modifying all the methods containing the conditional code.

    • The conditional code may be spread over several classes which make similar decisions about which algorithm to apply.

    Yet, solving this problem is feasible because:

    • The alternative behaviors are essentially interchangeable.

    Solution

    Apply the Strategy pattern, i.e., encapsulate the algorithmic dependent behavior into separate objects with polymorphic interfaces and delegate calls to these objects (see Figure \(\PageIndex{1}\)).

    Transformation to go from a state pattern simulated using explicit state conditional to a situation where the state pattern has been applied.
    Figure \(\PageIndex{1}\): Transformation to go from a state pattern simulated using explicit state conditional to a situation where the state pattern has been applied.

    Steps

    1. Identify the interface of the strategy class.

    2. Create a new abstract class, Strategy, representing the interface of the strategies.

    3. Create a new class subclass of Strategy for each identified algorithms.

    4. Define methods of the interface identified in Step 1 in each of the strategy classes by copying the corresponding code of the test to the method.

    5. Add a new instance variable in the Context class to refer to the current strategy.

    6. You may have to have a reference from the Strategy to the Context class to provide access to the information maintained by the Context (See difficulties).

    7. Initialize the newly created instance to refer to a default strategy instance.

    8. Change the methods of the Context class containing the tests by eliminating the tests and delegating the call to the instance variable.

    Step 4 can be performed using the Extract Method operation of the Refactoring Browser. Note that after each step, the regression tests should still run. The critical step is the last one, in which behavior is delegated to the new Strategy objects.

    Tradeoffs

    Pros

    • Limited Impact. The public interface of the original class does not have to change. Since the Strategy instances are accessed by delegation from the original object, the clients are unaffected. In a straight-forward case the application of this pattern has a limited impact on the clients. However, the Context interface will be reduced because all the previously implemented algorithms are now moved to Strategy classes. So you have to check the invocations of these methods and decide on a per case base.
    • After applying this pattern, you will be able to plug new strategies without impacting modifying the interface of the Context. Adding a new strategy does not require to recompile the Context class and its clients.

    • After applying this pattern, the interface of the Context class and the Strategy classes will be clearer.

    Cons

    • The systematic application of this pattern may lead to a class explosion. If you have 20 different algorithms you may not want to have 20 new classes each with only one method.

    • Object explosion. Strategies increase the number of instances in an application.

    Difficulties

    • There are several ways to share information between the Context and the Strategy objects, and the tradeoffs can be subtle. The information can be passed as argument when the Strategy method is invoked, the Context object itself can be passed as argument, or the Strategy objects can hold a reference to their context. If the relationship between the Context and the Strategy is highly dynamic, then it may be preferable to pass this information as a method argument. More detailed discussions of this issue exist in the literature on the Strategy pattern [GHJV95] [ABW98].

    Example

    The Design Patterns Smalltalk Companion [ABW98] presents a step-by-step code transformation.

    Related Patterns

    The symptoms and structure of Factor out Strategy bear comparison with Factor out State. The main difference consists in the fact that the Factor out State identifies behavior with different possible states of objects whereas Factor out Strategy is concerned with interchangeable algorithms that are independent of object state. Factor out Strategy allows one to add new strategies without impacting the existing strategy objects.


    This page titled 10.5: Factor out Strategy is shared under a CC BY-SA license and was authored, remixed, and/or curated by Serge Demeyer, Stéphane Ducasse, Oscar Nierstrasz.

    • Was this article helpful?