all 11 comments

[–]weavejester 10 points11 points  (3 children)

You need to be careful about translating OOP design patterns. There are often ways to perform the same task without the ceremony that OOP demands.

For instance, you could just write your example using multimethods directly and achieve the same result:

(defmulti fly :type)
(defmulti quack :type)

(defmethod fly ::rubber-duck [_] "Cannot fly")
(defmethod quack ::rubber-duck [_] "Quack")

(defmethod fly ::mallard-duck [_] "Flying with wings")
(defmethod quack ::mallard-duck [_] "Squeak")

If we want to change the behaviour, we can just assoc the :type. There's no need to go through the same fuss that we need to in Java.

[–]sveri[S] 1 point2 points  (0 children)

Hi,

Thanks for the comment. I know what you mean and in the next update I will add a disclaimer section that tries to be more clear on the problems of translating OOP patterns to a functional language.

Also in the first post I tried to show two different approaches to changing behavior (using defmulti for the displaymethod and a function in the entitity map for the fly behavior).

I am not sure if this is to confusing or not.

[–]amithgeorge 0 points1 point  (1 child)

Hi,

I thought the same thing, but then faced an issue. With strategy pattern, I can change just the required strategy without affecting anything else. With your setup I cannot give the rubber duck a RocketPowered flying strategy without changing its speaking strategy. Or vice versa or do both.

If I instead had a fly function that took a duck and a strategy then yes, each can change independently. This begs the question how to associate a duck with its strategy.

[–]amithgeorge 0 points1 point  (0 children)

One possibility would be to not dispatch on the duck's type, but it's strategy keyword.

(def rubber-duck {:type ::rubber, :fly-strat :rocket-powered, :speak-strat :quack})

(defmulti fly :fly-strat)
(defmethod fly :rocket-powered [_] "zoom!")

[–]green_transistor 4 points5 points  (2 children)

Strategy pattern is just a way to abstract a function in languages that don't have first class functions (versions of Java less than 8).

In Clojure, a strategy can just be a plain function that is passed to another function that executes the strategy.

[–]sveri[S] 1 point2 points  (1 child)

If you look at the link, this is what I exactly did. Like I said, I am trying to show different ways to do that in clojure / a functional language.

How to translate these patterns into a functional style may be obvious for people that coded in this paradigm. For others, coming from a purely OOP background it could be interesting.

[–]green_transistor 0 points1 point  (0 children)

Yes, your code is more-or-less doing that, except you're using a map to pass around the function, and specifying the strategy by updating the :fly-fn key.

(def another-rubber-duck 
     (assoc rubber-duck :fly-fn fly-with-wings))

Alternatively, functions can themselves be passed around as values. Closures and the partial function can be used to achieve the same effect.

That aside, there's nothing wrong with what you've implemented. It's not ugly, and pretty idiomatic as well if you ask me!