该战略的设计模式通常被认为是在缺乏这些语言的一流功能的替代品。
例如,说您想将功能传递给对象。在Java中,您必须向该对象传递另一个封装了所需行为的对象。在诸如Ruby之类的语言中,您只是以匿名函数的形式传递功能本身。
但是我在考虑它,因此决定也许Strategy提供的功能比普通的匿名功能还多。
这是因为对象可以保持独立于其方法运行时间的状态。但是,匿名函数本身只能保持在函数完成执行后就不复存在的状态。
在支持一流功能的面向对象语言中,策略模式相对于使用功能是否有任何优势?
该战略的设计模式通常被认为是在缺乏这些语言的一流功能的替代品。
例如,说您想将功能传递给对象。在Java中,您必须向该对象传递另一个封装了所需行为的对象。在诸如Ruby之类的语言中,您只是以匿名函数的形式传递功能本身。
但是我在考虑它,因此决定也许Strategy提供的功能比普通的匿名功能还多。
这是因为对象可以保持独立于其方法运行时间的状态。但是,匿名函数本身只能保持在函数完成执行后就不复存在的状态。
在支持一流功能的面向对象语言中,策略模式相对于使用功能是否有任何优势?
Answers:
当语言支持对函数的引用(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)
然后将全部由该策略实施。有些路由查找算法可能允许对其中一些用例进行内部优化,而某些可能不允许,因此实现者可以决定如何实现这些方法中的每一种。
另一种情况是该策略具有内部状态。当然,在某些语言中,闭包可以具有内部状态,但是当这种内部状态变得非常复杂时,将闭包提升为成熟的类通常变得更加优雅。
匿名函数只能保留在函数完成执行后不再存在的状态,这是不正确的。
以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在回答中所观察到的那样,根据情况(复杂状态)和编程语言,您可以通过使用对象来获得更优雅的解决方案。
let
然后在其中定义闭包。基本上,我会动态定义一个对象。在另一种语言(例如Java)中,定义合适的对象来保持状态可能更方便(从语法上讲更简单)。因此,我将视情况决定。
仅仅因为两个设计可以解决相同的问题,并不意味着它们是彼此的直接替代。
如果您需要在功能程序中跟踪状态,则即使语言允许,也不会突变close over变量。您安排调用一个以一个状态作为参数并返回新状态作为其返回值的函数。
您的体系结构看起来将非常不同,但是您将实现相同的目标。不要试图将一种范例的模式直接强加于另一种范例。
策略是一个概念,是解决特定的重复性问题的有用方法。它不是语言构造,也不是任何一种实现形式。闭包可用于一天执行策略,第二天执行观察者。
“ 战略” 一词在与其他程序员的对话中非常有用,以简洁地表达您的意图。没有什么神奇的。