Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the functional analogue of a Strategy pattern?

Disclaimer: I do not use functional languages; only trying to comprehend some parts of FP.

Google suggest the articles where first order functions with lambdas can offer the similar functionality which Strategy pattern gives.

Yet we somehow need to match data and corresponding lambda. With OO-design this is done automatically with Virtual Method Table (VMT), that is the type itself carries the important information needed to reason about execution flow making further addition of a new behavior easy (open closed principle): inherit and override. Old code simply stays unchanged. Functional pattern matching seems to be static in this regard and does not allow such kind of dynamics.

Sure, it is possible to code a configurable matching behavior for selecting lambda based on given data, but isn't it what we have in OOP out of the box?

like image 759
Pavel Voronin Avatar asked May 25 '15 12:05

Pavel Voronin


People also ask

What type of pattern is strategy pattern?

In computer programming, the strategy pattern (also known as the policy pattern) is a behavioral software design pattern that enables selecting an algorithm at runtime. Instead of implementing a single algorithm directly, code receives run-time instructions as to which in a family of algorithms to use.

What is the purpose of the strategy pattern?

The Strategy pattern lets you indirectly alter the object's behavior at runtime by associating it with different sub-objects which can perform specific sub-tasks in different ways. Use the Strategy when you have a lot of similar classes that only differ in the way they execute some behavior.

Why is it called strategy pattern?

The idea behind the strategy pattern is that "algorithms can be selected at runtime." (Wikipedia, Strategy Pattern) The pattern, in essence, is selecting the right strategy (or behavior) for solving a particular problem at runtime. Hence, it's name.

What problem does strategy pattern solve?

The strategy pattern is used to solve problems that might (or is foreseen they might) be implemented or solved by different strategies and that possess a clearly defined interface for such cases.


2 Answers

The simplest way, which is what I think most people are referring to when they talk about higher-order functions replacing the strategy pattern, is to pass the strategy in as an argument to your common code. Here's a Scala example that executes a strategy on two numbers, then multiplies the result by 3:

def commonCode(strategy: (Int, Int) => Int)(arg1: Int, arg2: Int) : Int = 
  strategy(arg1, arg2) * 3

You define your various strategies like this:

def addStrategy(arg1: Int, arg2: Int) : Int      = arg1 + arg2

def subtractStrategy(arg1: Int, arg2: Int) : Int = arg1 - arg2

Add call it like this:

commonCode(addStrategy)(2, 3)      // returns 15
commonCode(subtractStrategy)(2, 3) // returns -3

You can use partial application to avoid having to pass the strategy all over the place:

val currentStrategy = addStrategy _
...
val currentCommon = commonCode(currentStrategy)_
currentCommon(2, 3) // returns 15

This is so common we don't call it a strategy or a pattern. It's just basic functional programming. The strategy parameter to the commonCode function is like any other data. You can put it into a data structure with a bunch of other functions. You can use a closure or partial application to associate additional strategy-specific data. You can use a lambda like commonCode(_ / _) to avoid having to give your strategy a name.

like image 64
Karl Bielefeldt Avatar answered Oct 06 '22 06:10

Karl Bielefeldt


Here are two ways of implementing a simple Strategy pattern in Haskell. This is based on a simple OO example. It doesn't implement the different behaviors, it just shows you where they'd go.

Ex. 1: using data structures with hooks. Note that you specify the behavior you want when you create the Robot. Here, I created constructors that define the different configurations of Robot I want. The downside of this: these different kinds of robots share the same structure, so their implementations may be coupled.

module Main where

data Robot = Robot {
    moveImpl :: Robot -> IO Robot
    }

move :: Robot -> IO Robot
move r = (moveImpl r) r

aggressiveMove :: Robot -> IO Robot
aggressiveMove r = putStrLn "Find another robot, then attack it!" >> return r

defensiveMove :: Robot -> IO Robot
defensiveMove r = putStrLn "Find another robot, then run away from it!" >> return r

aggressiveRobot :: Robot
aggressiveRobot = Robot aggressiveMove

defensiveRobot :: Robot
defensiveRobot = Robot defensiveMove

main = do
    let robots = [aggressiveRobot, defensiveRobot]
    mapM_ move robots

Ex. 2: using type classes. This allows you to take totally different structures, representing different behaviors, and make them work in a uniform way. The downside: you can't just put them in a list, since Robot is no longer a data type that binds all different kinds of robots together.

module Main where

class Robot r where
    move :: r -> IO r

data AggressiveRobot = AggressiveRobot

aggressiveMove :: AggressiveRobot -> IO AggressiveRobot
aggressiveMove r = putStrLn "Find another robot, then attack it!" >> return r

instance Robot AggressiveRobot where
    move = aggressiveMove

data DefensiveRobot = DefensiveRobot

defensiveMove :: DefensiveRobot -> IO DefensiveRobot
defensiveMove r = putStrLn "Find another robot, then run away from it!" >> return r

instance Robot DefensiveRobot where
    move = defensiveMove

main = do
    let robotA = AggressiveRobot
        robotB = DefensiveRobot
    move robotA
    move robotB
like image 40
Owen S. Avatar answered Oct 06 '22 07:10

Owen S.