Defmulti to defprotocol conversion
Can the code below be converted to use defprotocol
both defrecord
instead of defmulti
and defmethod
?
(defmulti test-multimethod (fn [keyword] keyword))
(defmethod test-multimethod :foo [a-map]
"foo-method was called")
(defmethod test-multimethod :bar [a-map]
"bar-method was called")
(defmulti perimeter (fn [shape] (:shape-name shape)))
(defmethod perimeter :circle [circle]
(* 2 Math/PI (:radius circle)))
(defmethod perimeter :rectangle [rectangle]
(+ (* 2 (:width rectangle)) (* 2 (:height rectangle))))
(def some-shapes [{:shape-name :circle :radius 4}
{:shape-name :rectangle :width 2 :height 2}])
(defmulti area (fn [shape] (:shape-name shape)))
(defmethod area :circle [circle]
(* Math/PI (:radius circle) (:radius circle)))
(defmethod area :rectangle [rectangle]
(* (:width rectangle) (:height rectangle)))
(defmethod perimeter :square [square]
(* 4 (:side square)))
(defmethod area :square [square]
(* (:side square) (:side square)))
(def more-shapes (conj some-shapes
{:shape-name :square :side 4}))
(for [shape more-shapes] (perimeter shape))
(for [shape more-shapes] (area shape))
source to share
Yes, you declare your functions in the protocol definition Shape
and then define your implementations in different record implementations Square
, Circle
etc.
(defprotocol Shape
(area [this])
(perimeter [this]))
(defrecord Square [side] Shape
(area [this] (* (:side this) (:side this)))
(perimeter [this] (* 4 (:side this))))
(defrecord Rect [w l] Shape
(area [this] (* (:l this) (:w this)))
(perimeter [this] (+ (:l this) (:l this) (:w this) (:w this))))
(def s (->Square 4))
(def r (->Rect 2 5))
(map area [s r]) ; '(16 10)
(map :side [s r]) ; '(4 nil)
(map :l [s r]) ; '(nil 5)
Basically, it looks like OOP (but unchanged) if you're familiar with it.
One of the nice things about defmulti implementation for things like this, although you can often just serialize and deserialize your maps and use them as they are, without having to repeat them in a specific record class.
source to share