user977828
user977828

Reputation: 7679

Converting defmulti to defprotocol

Is it possible to convert the below code so it uses defprotocol and 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))

Upvotes: 3

Views: 157

Answers (1)

Dax Fohl
Dax Fohl

Reputation: 10781

Yes, you declare your functions in the protocol definition Shape, and then you define your implementations in your various 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)

Essentially this is just like OOP (but immutable) if you're familiar with that.

One nice thing about the defmulti implementation for things like this though, are you can often just serialize and deserialize your maps and use them as-is, without having to reify them into the specific record class.

Upvotes: 5

Related Questions