10.5: Factor out Strategy
- Page ID
- 32425
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}\)).
Steps
- Identify the interface of the strategy class.
- Create a new abstract class,
Strategy
, representing the interface of the strategies. - Create a new class subclass of
Strategy
for each identified algorithms. - 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.
- Add a new instance variable in the
Context
class to refer to the current strategy. - You may have to have a reference from the
Strategy
to theContext
class to provide access to the information maintained by theContext
(See difficulties). - Initialize the newly created instance to refer to a default strategy instance.
- 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, theContext
interface will be reduced because all the previously implemented algorithms are now moved toStrategy
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 theContext
class and its clients. - After applying this pattern, the interface of the
Context
class and theStrategy
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 theStrategy
objects, and the tradeoffs can be subtle. The information can be passed as argument when theStrategy
method is invoked, theContext
object itself can be passed as argument, or theStrategy
objects can hold a reference to their context. If the relationship between theContext
and theStrategy
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.