将defmulti转换为defprotocol

use*_*828 3 clojure

是否可以转换以下代码,使其使用defprotocoldefrecord代替defmultiand 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))
Run Code Online (Sandbox Code Playgroud)

Dax*_*ohl 5

是的,你在协议定义中声明你的功能Shape,然后在您的各种记录实现定义的实现SquareCircle等等。

(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)
Run Code Online (Sandbox Code Playgroud)

从本质上讲,如果您熟悉,这就像OOP(但不可变)一样。

defmulti实现的好处是,您通常可以序列化和反序列化地图,并按原样使用它们,而不必将其变成特定的记录类。