TheStrategyTemplatePattern

From Pattern Repository Wiki
Jump to: navigation, search

The Strategy Template Pattern

Contextual Forces

Motivation

You have redundant structure in the forms of switches spread across several existing classes and methods. All the cases in each switch are tightly coupled to the fact that you have a particular situation to handle (typically an object of a particular type). There are many problems with this. If you have to change one switch, you may have to change them all. With Shalloway's lawapplying, doing that without mistake is unlikely. This pattern is generally used as a transition to a better design - that is, a transition that takes place while reducing code debt.

Encapsulation

The Strategy Template pattern encapsulates the type of object being used from all of the switches involved.

Procedural Analog

We're replacing a series of ifs with a group of related strategies.

Non-Software Analog

When I teach a course there are many similar things I have to do - but how I do them depends upon the situation I am in. For example, I have the following actions:

* check with course owner
* get manuals ready
* get directions for course
* check out course layout
* put out materials

to name a few. Each of the implementations above will be done a little differently depending upon which situation I am in. A few of the cases I have to deal with:

* course at our offices
* public course
* course out of town
* course at a local company

Using this structure gives me a template for the actions, based (organized) around the cases I have.

Implementation Forces

Number of switches. Number of cases

Example Problem

class Foo {
  private int switchValue= 0;

  void setUpOfFoo () {
    switchValue= // somehow set
  }

  void someMethod1 () {

    . . .

    switch (switchValue) {  // we'll call this switch A
      case 1:
        // some code for switch in someMethod1 for case 1 in switch A
        break;

      case 2:
        // some code for switch in someMethod1 for case 2 in switch A
        break;

      . . .


      case n:
        // some code for switch in someMethod1 for case 1 in switch A
        break;
    }

  
    switch (switchValue) {  // we'll call this switch B
      case 1:
        // some code for switch in someMethod1 for case 1 in switch B
        break;

      case 2:
        // some code for switch in someMethod1 for case 2 in switch B
        break;

      . . .


      case n:
        // some code for switch in someMethod1 for case 1 in switch B
        break;
    }
    
      . . .
  }


  void someMethod2 () {

    . . .

    switch (switchValue) {  // we'll call this switch C
      case 1:
        // some code for switch in someMethod2 for case 1 in switch C
        break;

      case 2:
        // some code for switch in someMethod2 for case 2 in switch C
        break;

      . . .


      case n:
        // some code for switch in someMethod2 for case 1 in switch C
        break;
    }

  
    switch (switchValue) {  // we'll call this switch D
      case 1:
        // some code for switch in someMethod2 for case 1 in switch D
        break;

      case 2:
        // some code for switch in someMethod2 for case 2 in switch D
        break;

      . . .


      case n:
        // some code for switch in someMethod2 for case 1 in switch D
        break;
    }
    
      . . .
  }
}

Example Solution

class strategyTemplate {
  abstract void someMethod1A();
  abstract void someMethod1B();
  abstract void someMethod2C();
  abstract void someMethod2D();
  . . .
  abstract void someMethodnC();
  abstract void someMethodnD();
}

class strategyTemplate1 {
    void someMethod1A () {
        // some code for switch in someMethod1 for case 1 in switch A
    }
    void someMethod1B () {
        // some code for switch in someMethod1 for case 1 in switch B
    }  
    void someMethod1C () {
        // some code for switch in someMethod1 for case 1 in switch C
    }  
    void someMethod1D () {
        // some code for switch in someMethod1 for case 1 in switch D
    }  
}

class strategyTemplate2 {
    void someMethod2A () {
        // some code for switch in someMethod1 for case 2 in switch A
    }
    void someMethod2B () {
        // some code for switch in someMethod1 for case 2 in switch B
    }  
    void someMethod2C () {
        // some code for switch in someMethod1 for case 2 in switch C
    }  
    void someMethod2D () {
        // some code for switch in someMethod1 for case 2 in switch D
    }  
}

. . .

class strategyTemplaten {
    void someMethodnA () {
        // some code for switch in someMethod1 for case n in switch A
    }
    void someMethodnB () {
        // some code for switch in someMethod1 for case n in switch B
    }  
    void someMethodnC () {
        // some code for switch in someMethod1 for case n in switch C
    }  
    void someMethodnD () {
        // some code for switch in someMethod1 for case n in switch D
    }  
}

Now have the original code look like:

class Foo {
  private strategyTemplate myStrategyTemplate;

  void setUpOfFoo () {
    // getInstance is a static method that uses whatever 
    // set switchValue before
    myStrategyTemplate= stratgetTemplate.getInstance();  
  }

  void someMethod1 () {
    . . .
    myStrategyTemplate.someMethod1A();
    . . .
    myStrategyTemplate.someMethod1B();
    . . .
  }

  void someMethod2 () {
    . . .
    myStrategyTemplate.someMethod2C();
    . . .
    myStrategyTemplate.someMethod2D();
    . . .
  }
}


Cost-Benefit (gain-loss)

Notice how Foo becomes much more readable. Also, if you add a new case, you simply make a new implementation or extension of strategyTemplate and fill in the blanks.

I call this ‘strategyTemplate’ even though the implementation looks like a cross between a strategy and a bridge. The motiviation is more like a cross between a strategy and a template. In fact, another way to implement this would be to use a template method patter, but that works best for one class and is often difficult to do in situations like this.

The Strategy Template Pattern greatly reduces the verbosity of the main program using service objects. It also organizes the service methods into a way to avoid Shalloway's Law. From here, further code debt reductions can be made.