战略模式的功能类比是什么?

时间:2015-05-25 12:22:53

标签: design-patterns functional-programming

免责声明:我不使用函数式语言;只是试图理解FP的某些部分。

Google建议使用lambdas的第一顺序函数可以提供与策略模式相似的功能的文章。

然而,我们需要匹配数据和相应的lambda。使用OO-design,这是使用Virtual Method Table (VMT)自动完成的,即类型本身带有推理执行流程所需的重要信息,从而使新行为更容易添加(开放封闭原则):继承和覆盖。旧代码只是保持不变。在这方面,功能模式匹配似乎是静态的,并且不允许这种动态。

当然,可以编写一个可配置的匹配行为来根据给定的数据选择lambda,但这不是我们开箱即用的OOP吗?

2 个答案:

答案 0 :(得分:8)

最简单的方法,就是我认为大多数人在谈论取代策略模式的高阶函数时所指的是将策略作为参数传递给您的公共代码。这是一个Scala示例,它对两个数字执行策略,然后将结果乘以3:

NetworkCredential netCred = new NetworkCredential("username", "password");           
TfsTeamProjectCollection server = new TfsTeamProjectCollection(new Uri("https://someproject.visualstudio.com/DefaultCollection")), netCred);
server.Authenticate();

您可以定义各种策略:

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

添加如下调用:

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

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

您可以使用部分应用程序来避免必须在整个地方传递策略:

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

这很常见,我们不称之为策略或模式。它只是基本的函数式编程。 val currentStrategy = addStrategy _ ... val currentCommon = commonCode(currentStrategy)_ currentCommon(2, 3) // returns 15 函数的strategy参数与任何其他数据一样。您可以将其放入具有许多其他功能的数据结构中。您可以使用闭包或部分应用程序来关联其他特定于策略的数据。您可以使用像commonCode这样的lambda来避免为策略命名。

答案 1 :(得分:1)

以下是在Haskell中实现简单策略模式的两种方法。这是基于simple OO example。它没有实现不同的行为,只是告诉你他们去哪里。

<强>实施例。 1:使用带钩子的数据结构。请注意,您指定创建Robot时所需的行为。在这里,我创建了构造函数,用于定义我想要的Robot的不同配置。其缺点是:这些不同类型的机器人共享相同的结构,因此它们的实现可能是耦合的。

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

<强>实施例。 2:使用类型类。这允许您采用完全不同的结构,表示不同的行为,并使它们以统一的方式工作。缺点:你不能把它们放在一个列表中,因为Robot不再是将所有不同类型的机器人绑在一起的数据类型。

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