15

The Strategy design pattern is often regarded as a substitute for first-class functions in languages that lack them.

So for example say you wanted to pass functionality into an object. In Java you'd have to pass in the object another object which encapsulates the desired behavior. In a language such as Ruby, you'd just pass the functionality itself in the form of an anonymous function.

However I was thinking about it and decided that maybe Strategy offers more than a plain anonymous function does.

This is because an object can hold state that exists independently of the period when it's method runs. However an anonymous function by itself can only hold state that ceases to exist the moment the function finishes execution.

In an object-oriented language that supports first-class functions, does the strategy pattern have any advantage over using functions?

  • 10
    "However an anonymous function by itself can only hold state that ceases to exist the moment the function finishes execution.": This is not true: a closure can hold state that lives across different invocations. – Giorgio Aug 16 '14 at 11:37
  • "However an anonymous function by itself can only hold state that ceases to exist the moment the function finishes execution." : not forgetting global variables, and static variables at the very least. – gbjbaanb Aug 16 '14 at 12:29
  • 5
    Related : c2.com/cgi/wiki?ClosuresAndObjectsAreEquivalent – Euphoric Aug 16 '14 at 13:15
13

When the language supports references to function (Java does since version 8), these are often a good alternative for strategies, because they usually express the same thing with less syntax. However, there are some cases where a real object can be useful.

A Strategy interface can have multiple methods. Let's take an interface RouteFindingStragegy as example, which encapsulates different route finding algorithms. It could declare methods like

  • 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)

which then would all be implemented by the strategy. Some route-finding algorithms might allow internal optimizations for some of these use-cases and some might not, so the implementor can decide how to implement each of these methods.

Another case is when the strategy has an inner state. Sure, in some languages closures can have inner state, but when this inner state gets very complex, it often becomes more elegant to promote the closure to a full-fledged class.

  • 2
    "when this inner state gets very complex, it often becomes useful to promote the closure to a full-fledged class": Why? If the inner state gets complex, you can also put it in an object / record that is stored inside the closure. – Giorgio Aug 16 '14 at 11:57
  • 1
    @Giorgio But then you would have two syntax entities to maintain - the closure and the class which manages its internal state. So the code could be simplified by moving the closure to that class. This might be better or it might not, which depends on the exact use-case, the programming language and personal preference. – Philipp Aug 16 '14 at 12:02
  • It seems a reasonable view to me. – Giorgio Aug 16 '14 at 14:51
5

It is not true that an anonymous function can only hold state that ceases to exist when the function finishes execution.

Take the following example in Common Lisp:

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

This function takes a list of strings and prepends a counter to each element of the list. So, for example, invoking

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

gives

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

The function number-strings internally uses an anonymous function with a variable counter that holds state (the current value of the counter) which is reused every time the function is invoked.

In general, you can think of a closure as an object with only one method. Alternatively, an object is a collection of closures that share the same closed-over variables. So I am not sure whether there are cases in which you need to use an object instead of a closure: I would argue that both are ways of looking at the same pattern from different perspectives.

In particular, the strategy pattern requires an object with only one method, so a closure should do the job. But, as Philipp has observed in his answer, depending on the circumstances (complex state) and programming languages you may get a more elegant solution by using objects.

  • So in a language that supports first class functions as closures, would you ever still use the 'classic' Strategy? – Aviv Cohn Aug 16 '14 at 12:01
  • 1
    I tend to agree with Philipp: it depends on the language and personal preferences. I would always choose the approach that makes the notation as simple as possible. For example, in Lisp I could define my state as a list of variables through a let and then define my closure inside it. Basically, I would have defined an object with one method on the fly. In another language (e.g. Java) it might be more convenient (syntactically simpler) to define a proper object to hold the state. So, I would decide from case to case. – Giorgio Aug 16 '14 at 12:09
1

Just because two designs can solve the same problem doesn't mean they are direct substitutions for each other.

If you need to track state in a functional program, you don't mutate a closed over variable, even if the language allows it. You arrange to call a function that takes in one state as an argument and returns the new state as its return value.

Your architecture will look very different, but you'll accomplish the same goal. Don't try to force one paradigm's patterns directly onto the other one.

  • "If you need to track state in a functional program, you don't mutate a closed over variable, even if the language allows it.": I am a fan of purely functional style and I agree with your advice. On the other hand, closures are not only a functional construct, let alone purely functional. The idea of closing over variables from the lexical context is orthogonal to referential transparency / immutability. – Giorgio Aug 17 '14 at 20:10
  • Sorry, but I can not follow your argumentation. What does state handling in purely functional programming have to do with the question at hand? – Philipp Aug 17 '14 at 22:08
  • 1
    The point is if you use one part of a functional paradigm, like first class functions, don't be surprised if you need to pull in other parts of the paradigm to make it work smoothly. – Karl Bielefeldt Aug 17 '14 at 22:48
1

Strategy is a concept, a useful recipe for solving a particular, recurring problem. It's not a language construct, nor is it about any one form of implementation. A closure can be used to implement Strategy one day and Observer the next day.

The term Strategy is mostly useful in conversations with other programmers to concisely express your intent. There's nothing magical about it.

  • 2
    The question specifically mentions the strategy design pattern which has a specific class structure. The other meaning of "strategy" as a plan of action intended to accomplish a specific goal is inaccurate in this context. – user22815 Aug 28 '14 at 3:54
  • I'm inclined to agree with @Snowman. Are you sure you are talking about the strategy pattern? – Rowan Freeman Aug 28 '14 at 4:18
  • 1
    @Snowman, even the page you linked to doesn't state exactly how this pattern should be implemented, rather they give examples in specific languages, but there's nothing in the UML diagram that says I need to use C++ inheritance, Java interfaces or Ruby blocks. So I kindly disagree with your analysis. – idoby Aug 30 '14 at 16:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.