Chapter 5. Behavioral Patterns – Strategy, State, and Template Method

I hope you're still with us; now, we will introduce you to the third and last category of the design patterns, which is categorized as a Gang of Four (GoF) design patterns: the behavioral pattern. Behavioral patterns are dedicated to algorithms and communication between them.

As algorithms consist of several operations that are divided into different classes, behavioral patterns can handle the organization of such classes and the ways in which they can communicate with one another.

The behavioral category contains 11 patterns that we will discuss through four chapters. In this chapter, we will discuss the following three patterns:

  • The strategy pattern
  • The state pattern
  • The template method pattern

The strategy pattern

When you need to change part of an object's algorithm at runtime, without modifying the client, the strategy pattern is the appropriate pattern to be used.

It removes an algorithm from its host class and moves it to a separate class. The algorithm part that can change is the strategy. Every strategy uses the same interface. The class using the strategy pattern delegates the treatment of the algorithm to the strategy.

Roles

The strategy pattern is used to create an interchangeable family of algorithms from which the required process is chosen at runtime.

The algorithm changes don't affect the client part. This pattern can be used in the following cases:

  • The behavior of a class can be implemented by different algorithms where some of them are better in terms of execution time or memory consumption
  • Choosing the appropriate algorithm with if conditions instruction complexify the code
  • A system has similar classes where only the behavior changes; in this case, the strategy pattern allows you to group these classes in only one class, which greatly simplifies the interface for clients

Design

The generic structure of the strategy platform is as follows:

Design

Participants

The participants in the strategy pattern are as follows:

  • IStrategy: This class defines the common interface implemented by all algorithms. This is the interface used by the ClassUsingStrategy class to invoke the right algorithm.
  • ConcreteStrategyA and ConcreteStrategyB: These are concrete classes that implement different algorithms based on the IStrategy interface.
  • ClassUsingStrategyA and ClassUsingStrategyB: These are classes that use an algorithm from classes that implement the IStrategy interface. These classes have a reference to one instance of one of the concrete strategy classes. These classes can expose some internal data to the implementation classes.

Collaboration

The ClassUsingStrategy and ConcreteStrategy classes interact to implement algorithms.

In most cases, data needed by the algorithms is sent as arguments to the constructor but can be sent by a set property too. If needed, the ClassUsingStrategy class can provide you some methods to allow you to access its internal data.

The Client instance will initiate ClassUsingStrategy with a Strategy object and call the ClassUsingStrategy method that uses the strategy pattern.

Then, this class will send the request received from the client to the instance that is referenced by the strategy attribute.

Illustration

We will see how to implement the strategy pattern using a simple example.

Some objects can move, but some of them don't move in the same manner. Each object has a particular manner in which it moves: some can walk and others can run and fly. The move behavior is our strategy, and we will encapsulate the performMove() action in the concrete strategy class that is referenced in the concrete class using the strategy.

Implementation

Open the strategy pattern Xcode project to see the organization of our code. The participants in this pattern are separated in four folders, as shown in the following screenshot:

Implementation

The first thing that we will do is define the interface of the move strategy and then describe AbstractClassUsingMoveStrategy.

The interface of the move strategy is really easy; we will only tell the class implementing the strategy that we are waiting for a performMove method.

The MoveStrategyProtocol.swift file is as follows:

//Common Interface used by algorithms
protocol MoveStrategyProtocol {
  func performMove()
}

Our abstract class needs to have a role to keep a reference to the strategy that will be applied by encapsulating the call to the performMove method of the current strategy into its own method that we will call move(). The instance of the strategy object will be received in the constructor of the class implementing the AbstractObjectThatMove class.

For our demo, we will add an internal computed property, WhoAmI, where we will set or get the name of the concrete class using the strategy pattern.

Our AbstractObjectThatMove.swift file is as follows:

class AbstractObjectThatMove {
  private var strategy: MoveStrategyProtocol!
  private var whoAmI:String = "Unknown Object"
  
  required init(strategy: MoveStrategyProtocol) {
    self.strategy = strategy
  }
  
  func move(){
    strategy.performMove()
  }
  
  internal var WhoAmi: String {
    get {
      return whoAmI
    }
    set {
      whoAmI = newValue
    }
  }
}

Note

Swift 2.0 has no support for abstract classes as yet. Here, we named the AbstractObjectThatMove class, even if it is not a "real" abstract class, only to be as close as possible to the general concept of the pattern. Nevertheless, there might be a way to have something that seems like an abstract class, but it differs from the general concept of the pattern where we pass a strategy to the initializer. Swift 2.0 has protocol extensions that give the opportunity to add partial implemented methods to protocols, and in "some way", make an abstract class:

protocol AbstractObjectThatMove {
   var WhoAmi: String { get set}
}
extension AbstractObjectThatMove {
    func move(strategy: IMoveStrategy) {
       strategy.performMove()
    }
}
class Human : AbstractObjectThatMove {
    var WhoAmi: String = "i'm a human"
}

Then, we can process the following code:

print("- *** working with Human")
let strategyForHuman = WalkMoveStrategy()
let human = Human()

// Tell who am I
print(human.WhoAmi)

//perform human move:
human.move(strategyForHuman)

So, we come back to the implementation of the pattern. In our current example, we have three concrete classes that implement the abstract classes: Rabbit, Bird, and Human. As the implementation is the same for all these three objects, I will only display the implementation of the concrete Human class:

class Human: AbstractObjectThatMove {
  required init(strategy: MoveStrategyProtocol){
    super.init(strategy: strategy)
    self.WhoAmi = "i'm a human"
  }
}

We want to add a value to the WhoAmI property while it is initializing, so we inform the init method that this method is required. We then set the value of the property with a simple self.WhoAmi = "I'm a human" statement.

Note

We could have done this by adding an additional parameter to the constructor defined in the AbstractClass that takes the value of WhoAmI and assigns it to the internal whoAmI variable too.

Now, we will implement WalkMoveStrategy, as defined in the MoveStrategyProtocol protocol:

class WalkMoveStrategy: MoveStrategyProtocol {
  func performMove() {
    print("I am walking")
  }
}

There's nothing complex here; we implement the performMove method and print the message of the move action. The RunMoveStrategy and FlyMoveStrategy methods are implemented in the same way; just the print statement changes:

Last, to complete the example, we will make our human, bird, and rabbit perform a move according to the strategy they apply:print("- *** working with Human")
let strategyForHuman = WalkMoveStrategy()
let human = Human(strategy: strategyForHuman)

// Tell who am I
print(human.WhoAmi)

//perform human move:
human.move()


print("- *** working with Bird")
let strategyForBid = FlyMoveStrategy()
let bird = Bird(strategy: strategyForBid)

// Tell who am I
print(bird.WhoAmi)

//perform human move:
bird.move()

print("- *** working with Rabbit")
let strategyForRabbit = RunMoveStrategy()
let rabbit = Rabbit(strategy: strategyForRabbit)

// Tell who am I
print(rabbit.WhoAmi)

//perform human move:
rabbit.move()

In the preceding highlighted code, you can see how we proceeded to apply the strategy to the Human class using the following steps:

  1. First, we instantiate a strategy.
  2. Next, we instantiate a concrete class using the strategy pattern where the strategy declared first is sent as an argument.
  3. Then, we perform a move action in the concrete class. The move action invokes the performMove method of the strategy that the concrete class has in reference.
  4. Click on the Run button to see the result.

You should see the following result in the console of Xcode:

Implementation
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset