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

Pav*_*nin 4 design-patterns functional-programming

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

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

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

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

Kar*_*ldt 10

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

def commonCode(strategy: (Int, Int) => Int)(arg1: Int, arg2: Int) : Int = 
  strategy(arg1, arg2) * 3
Run Code Online (Sandbox Code Playgroud)

您可以定义各种策略:

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

def subtractStrategy(arg1: Int, arg2: Int) : Int = arg1 - arg2
Run Code Online (Sandbox Code Playgroud)

添加如下调用:

commonCode(addStrategy)(2, 3)      // returns 15
commonCode(subtractStrategy)(2, 3) // returns -3
Run Code Online (Sandbox Code Playgroud)

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

val currentStrategy = addStrategy _
...
val currentCommon = commonCode(currentStrategy)_
currentCommon(2, 3) // returns 15
Run Code Online (Sandbox Code Playgroud)

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

  • 策略模式实际上并不需要多态性,只需具有相同签名的功能即可。当您的语言不轻易支持高阶函数时,多态性是一种解决方法。FP中如何实现多态确实是一个单独的问题。 (2认同)

Owe*_* S. 1

以下是在 Haskell 中实现简单策略模式的两种方法。这是基于一个简单的 OO 示例。它不会实现不同的行为,它只是向您展示它们的去向。

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

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
Run Code Online (Sandbox Code Playgroud)

前任。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
Run Code Online (Sandbox Code Playgroud)