The strategy pattern

The strategy pattern enables clients to select the best algorithm to use at runtime. Instead of coupling the client with predefined algorithms, the client can be configured with a specific algorithm (strategy) when necessary. In addition, sometimes the choice of algorithm cannot be determined ahead of time because the decision may depend on the input data, the environment, or something else.

In Julia, we can solve the problem using multiple dispatch. Let's consider the case of a Fibonacci sequence generator. As we learned from Chapter 6, Performance Patterns, the calculation of the nth Fibonacci number can be tricky when we implement it recursively, so our first algorithm (strategy) may be memoization. In addition, we can also solve the same problem using an iterative algorithm without using any recursion. 

In order to support both memoization and iterative algorithms, let's create some new types as follows:

abstract type Algo end
struct Memoized <: Algo end
struct Iterative <: Algo end

The Algo abstract type is the supertype for all Fibonacci algorithms. At the moment, we only have two algorithms to choose from: Memoized or Iterative. Now, we can define the memoized version of the fib function as follows:

using Memoize
@memoize function _fib(n)
n <= 2 ? 1 : _fib(n-1) + _fib(n-2)
end

function fib(::Memoized, n)
println("Using memoization algorithm")
_fib(n)
end

A memoized function _fib is first defined. Then a wrapper function fib is defined, taking a Memoized object as the first argument. The corresponding iterative algorithm can be implemented as follows:

function fib(algo::Iterative, n) 
n <= 2 && return 1
prev1, prev2 = 1, 1
local curr
for i in 3:n
curr = prev1 + prev2
prev1, prev2 = curr, prev1
end
return curr
end

How the algorithm actually works is unimportant in this discussion. As the first argument is an Iterative object, we know that this function will be dispatched accordingly. 

From the client's perspective, it can choose either the memoized version or the iterative function, depending on what it needs. As the memoized version runs at O(1) speed, it should be faster when n is large; however, for a small value of n, the iterative version would work better. We can call the fib function in one of the following ways:

fib(Memoized(), 10)
fib(Iterative(), 10)

Should the client choose to implement an algorithm-selection process, it can be done easily, as follows:

function fib(n)
algo = n > 50 ? Memoized() : Iterative()
return fib(algo, n)
end

The successful test result is shown here:

As you can see, implementing the strategy pattern is quite easy. The unreasonable effectiveness of multiple dispatch has come to rescue again! Next, we will go over another behavioral pattern called the template method.

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

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