一流的功能是否可以代替战略模式?


15

战略的设计模式通常被认为是在缺乏这些语言的一流功能的替代品。

例如,说您想将功能传递给对象。在Java中,您必须向该对象传递另一个封装了所需行为的对象。在诸如Ruby之类的语言中,您只是以匿名函数的形式传递功能本身。

但是我在考虑它,因此决定也许Strategy提供的功能比普通的匿名功能还多。

这是因为对象可以保持独立于其方法运行时间的状态。但是,匿名函数本身只能保持在函数完成执行后就不复存在的状态。

在支持一流功能的面向对象语言中,策略模式相对于使用功能是否有任何优势?


10
“但是,匿名函数本身只能保存在函数完成执行后就不存在的状态。”:事实并非如此:闭包可以保存跨不同调用的状态。
乔治(Giorgio)2014年

“但是,匿名函数本身只能保持在函数完成执行后就不复存在的状态。” :至少不要忘记全局变量和静态变量。
gbjbaanb 2014年

Answers:


13

当语言支持对函数的引用(Java从版本8开始)时,它们通常是策略的不错选择,因为它们通常用较少的语法表示相同的东西。但是,在某些情况下,实际对象可能会有用。

策略接口可以有多种方法。让我们以一个接口RouteFindingStragegy为例,该接口封装了不同的路由查找算法。它可以声明像

  • Route findShortestRoute(Node start, Node destination)
  • boolean doesRouteExist(Node start, Node destination)
  • Route[] findAllPossibleRoutes(Node start, Node destination)
  • Route findShortestRouteToClosestDestination(Node start, Node[] destinations)
  • Route findTravelingSalesmanRoute(Node[] stations)

然后将全部由该策略实施。有些路由查找算法可能允许对其中一些用例进行内部优化,而某些可能不允许,因此实现者可以决定如何实现这些方法中的每一种。

另一种情况是该策略具有内部状态。当然,在某些语言中,闭包可以具有内部状态,但是当这种内部状态变得非常复杂时,将闭包提升为成熟的类通常变得更加优雅。


2
“当这种内部状态变得非常复杂时,将封闭状态提升为成熟的类通常变得很有用”:为什么?如果内部状态变得复杂,您还可以将其放在存储在闭包内部的对象/记录中。
Giorgio 2014年

1
@Giorgio但是,您将需要维护两个语法实体-闭包和管理其内部状态的类。因此,可以通过将闭包移动到该类来简化代码。这可能会更好,也可能不会,这取决于确切的用例,编程语言和个人喜好。
菲利普

在我看来,这是一个合理的看法。
Giorgio 2014年

5

匿名函数只能保留在函数完成执行后不再存在的状态,这是不正确的。

以Common Lisp中的以下示例为例:

(defun number-strings (ss)
  (let ((counter 0))
    (mapcar #'(lambda (s) (format nil "~a: ~a" (incf counter) s)) ss)))

该函数获取字符串列表,并在列表的每个元素前添加一个计数器。因此,例如,调用

(number-strings '("a" "b" "c"))

("1: a" "2: b" "3: c")

该函数在number-strings内部使用一个匿名函数,该函数具有一个counter保存状态(计数器的当前值)的变量,该变量在每次调用该函数时都会被重用。

通常,仅使用一种方法就可以将闭包视为对象。或者,对象是共享相同封闭变量的闭包的集合。因此,我不确定是否存在需要使用对象而不是闭包的情况:我认为这两者都是从不同角度看待相同模式的方式。

特别是,策略模式只需要一个对象就可以使用一种方法,因此闭包应该可以完成任务。但是,正如Philipp在回答中所观察到的那样,根据情况(复杂状态)和编程语言,您可以通过使用对象来获得更优雅的解决方案。


因此,在支持一流功能作为闭包的语言中,您是否还会使用“经典”策略?
阿维夫·科恩

1
我倾向于同意Philipp:这取决于语言和个人喜好。我将始终选择使表示法尽可能简单的方法。例如,在Lisp中,我可以通过a将状态定义为变量列表,let然后在其中定义闭包。基本上,我会动态定义一个对象。在另一种语言(例如Java)中,定义合适的对象来保持状态可能更方便(从语法上讲更简单)。因此,我将视情况决定。
Giorgio 2014年

1

仅仅因为两个设计可以解决相同的问题,并不意味着它们是彼此的直接替代。

如果您需要在功能程序中跟踪状态,则即使语言允许,也不会突变close over变量。您安排调用一个以一个状态作为参数并返回新状态作为其返回值的函数。

您的体系结构看起来将非常不同,但是您将实现相同的目标。不要试图将一种范例的模式直接强加于另一种范例。


“如果需要在功能程序中跟踪状态,则即使语言允许,也不会突变closed变量。”:我是纯函数风格的支持者,我同意您的建议。另一方面,闭包不仅是功能构造,更let论是纯粹的功能。从词法上下文中封闭变量的想法与参照透明性/不变性正交。
Giorgio 2014年

对不起,但我不能听从您的论点。纯函数式编程中的状态处理与手头的问题有什么关系?
菲利普

1
关键是,如果您使用功能范式的一部分,例如一流的功能,那么如果需要引入范式的其他部分以使其平稳运行,请不要感到惊讶。
Karl Bielefeldt 2014年

1

策略是一个概念,是解决特定的重复性问题的有用方法。它不是语言构造,也不是任何一种实现形式。闭包可用于一天执行策略,第二天执行观察者。

“ 战略” 一词在与其他程序员的对话中非常有用,以简洁地表达您的意图。没有什么神奇的。


2
这个问题专门提到具有特定类别结构的策略设计模式。在这种情况下,“战略”作为旨在实现特定目标的行动计划的其他含义是不准确的。

我倾向于同意@Snowman。您确定要谈论策略模式吗?
罗文·弗里曼

1
@Snowman,即使您链接到的页面也没有确切说明应如何实现此模式,而是以特定语言提供示例,但是UML图中没有任何内容表明我需要使用C ++继承,Java接口或Ruby块。因此,我不同意您的分析。
idoby 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.