多态与多重方法 #
一、多重方法简介 #
多重方法是Clojure实现运行时多态的机制。与面向对象语言的多态不同,Clojure的多重方法基于任意函数的返回值进行派发,更加灵活。
1.1 基本概念 #
clojure
(defmulti greet :type)
(defmethod greet :person [entity]
(str "Hello, " (:name entity)))
(defmethod greet :robot [entity]
(str "Beep boop, I am " (:name entity)))
(greet {:type :person :name "Alice"})
(greet {:type :robot :name "R2D2"})
1.2 组成部分 #
| 部分 | 说明 |
|---|---|
defmulti |
定义多重方法,指定派发函数 |
defmethod |
定义特定派发值的方法实现 |
| 派发函数 | 决定调用哪个方法的函数 |
| 派发值 | 派发函数返回的值,用于匹配方法 |
二、定义多重方法 #
2.1 defmulti #
clojure
(defmulti name dispatch-fn)
(defmulti area :shape)
(defmulti calculate (fn [x y] (class x)))
(defmulti process first)
2.2 defmethod #
clojure
(defmethod area :circle [shape]
(* Math/PI (:radius shape) (:radius shape)))
(defmethod area :rectangle [shape]
(* (:width shape) (:height shape)))
(area {:shape :circle :radius 5})
(area {:shape :rectangle :width 10 :height 5})
2.3 默认方法 #
clojure
(defmulti greet :type)
(defmethod greet :person [entity]
(str "Hello, " (:name entity)))
(defmethod greet :default [entity]
(str "Unknown entity: " entity))
(greet {:type :person :name "Alice"})
(greet {:type :alien :name "ET"})
三、派发函数 #
3.1 关键字派发 #
clojure
(defmulti draw :shape)
(defmethod draw :circle [shape]
(str "Drawing circle with radius " (:radius shape)))
(defmethod draw :rectangle [shape]
(str "Drawing rectangle " (:width shape) "x" (:height shape)))
3.2 函数派发 #
clojure
(defmulti process (fn [x] (class x)))
(defmethod process String [s]
(str "String: " s))
(defmethod process Integer [n]
(str "Integer: " n))
(defmethod process :default [x]
(str "Unknown: " (class x)))
(process "hello")
(process 42)
(process 3.14)
3.3 多参数派发 #
clojure
(defmulti calculate (fn [a b] [(class a) (class b)]))
(defmethod calculate [String String] [a b]
(str a b))
(defmethod calculate [Integer Integer] [a b]
(+ a b))
(defmethod calculate [Integer String] [a b]
(str a b))
(calculate "Hello " "World")
(calculate 1 2)
(calculate 1 " apple")
3.4 复杂派发 #
clojure
(defmulti classify
(fn [item]
(cond
(and (map? item) (:error item)) :error
(map? item) :success
(coll? item) :collection
:else :unknown)))
(defmethod classify :error [item]
(str "Error: " (:message item)))
(defmethod classify :success [item]
(str "Success: " (:data item)))
(defmethod classify :collection [item]
(str "Collection with " (count item) " items"))
(classify {:error true :message "Failed"})
(classify {:data "result"})
(classify [1 2 3])
四、层次结构 #
4.1 derive #
clojure
(derive ::dog ::animal)
(derive ::cat ::animal)
(derive ::husky ::dog)
(isa? ::husky ::dog)
(isa? ::husky ::animal)
(isa? ::cat ::dog)
4.2 层次结构派发 #
clojure
(defmulti speak :type)
(defmethod speak ::animal [entity]
(str (name (:type entity)) " makes a sound"))
(defmethod speak ::dog [entity]
(str (name (:type entity)) " barks"))
(defmethod speak ::cat [entity]
(str (name (:type entity)) " meows"))
(speak {:type ::dog :name "Buddy"})
(speak {:type ::husky :name "Max"})
(speak {:type ::cat :name "Whiskers"})
4.3 parents和ancestors #
clojure
(parents ::husky)
(ancestors ::husky)
(descendants ::animal)
4.4 make-hierarchy #
clojure
(def my-hierarchy (make-hierarchy))
(def my-hierarchy
(-> my-hierarchy
(derive :mammal :animal)
(derive :dog :mammal)
(derive :cat :mammal)))
(defmulti speak-h :type :hierarchy #'my-hierarchy)
(defmethod speak-h :animal [entity]
"Some sound")
(defmethod speak-h :dog [entity]
"Woof!")
五、方法组合 #
5.1 prefer-method #
当多个方法匹配时,指定优先级:
clojure
(defmulti process :type)
(defmethod process :animal [x]
(str "Animal: " (:name x)))
(defmethod process :pet [x]
(str "Pet: " (:name x)))
(prefer-method process :pet :animal)
(derive ::dog :animal)
(derive ::dog :pet)
(process {:type ::dog :name "Buddy"})
5.2 methods和get-method #
clojure
(defmulti greet :type)
(defmethod greet :person [x] "Hello person")
(defmethod greet :robot [x] "Beep boop")
(methods greet)
(get-method greet :person)
5.3 remove-method #
clojure
(defmulti test-multi :type)
(defmethod test-multi :a [x] "A")
(remove-method test-multi :a)
(get-method test-multi :a)
六、实践示例 #
6.1 序列化系统 #
clojure
(defmulti serialize :format)
(defmethod serialize :json [data]
(str "{\"value\": " (:value data) "}"))
(defmethod serialize :xml [data]
(str "<value>" (:value data) "</value>"))
(defmethod serialize :edn [data]
(str {:value (:value data)}))
(serialize {:format :json :value 42})
(serialize {:format :xml :value 42})
(serialize {:format :edn :value 42})
6.2 图形渲染 #
clojure
(defmulti render :shape)
(defmethod render :circle [{:keys [radius center]}]
(str "Circle at " center " with radius " radius))
(defmethod render :rectangle [{:keys [width height position]}]
(str "Rectangle " width "x" height " at " position))
(defmethod render :triangle [{:keys [vertices]}]
(str "Triangle with vertices " vertices))
(render {:shape :circle :radius 5 :center [0 0]})
(render {:shape :rectangle :width 10 :height 5 :position [1 1]})
6.3 支付处理 #
clojure
(defmulti process-payment :method)
(defmethod process-payment :credit-card [{:keys [card-number amount]}]
{:status :success
:message (str "Charged $" amount " to card ending in "
(subs card-number (- (count card-number) 4)))})
(defmethod process-payment :paypal [{:keys [email amount]}]
{:status :success
:message (str "Charged $" amount " via PayPal to " email)})
(defmethod process-payment :bank-transfer [{:keys [account amount]}]
{:status :pending
:message (str "Transfer of $" amount " initiated to account " account)})
(process-payment {:method :credit-card :card-number "1234567890123456" :amount 100})
(process-payment {:method :paypal :email "user@example.com" :amount 50})
6.4 数据验证 #
clojure
(defmulti validate :type)
(defmethod validate :string [{:keys [value min-len max-len]}]
(cond
(< (count value) (or min-len 0))
{:valid false :error "Too short"}
(> (count value) (or max-len Integer/MAX_VALUE))
{:valid false :error "Too long"}
:else
{:valid true}))
(defmethod validate :number [{:keys [value min max]}]
(cond
(< value (or min Integer/MIN_VALUE))
{:valid false :error "Too small"}
(> value (or max Integer/MAX_VALUE))
{:valid false :error "Too large"}
:else
{:valid true}))
(validate {:type :string :value "hello" :min-len 1 :max-len 10})
(validate {:type :number :value 5 :min 1 :max 10})
七、多重方法vs协议 #
7.1 对比 #
| 特性 | 多重方法 | 协议 |
|---|---|---|
| 派发方式 | 任意函数 | 类型 |
| 性能 | 较慢 | 较快 |
| 灵活性 | 高 | 中 |
| 适用场景 | 复杂派发逻辑 | 类型派发 |
7.2 选择建议 #
使用多重方法当:
- 需要复杂派发逻辑
- 需要基于多个参数派发
- 需要层次结构派发
使用协议当:
- 基于类型派发
- 需要高性能
- 面向对象风格
八、总结 #
多重方法要点:
| 概念 | 说明 |
|---|---|
defmulti |
定义多重方法和派发函数 |
defmethod |
定义方法实现 |
:default |
默认方法 |
derive |
建立层次关系 |
prefer-method |
解决冲突 |
关键点:
- 多重方法实现运行时多态
- 派发函数决定调用哪个方法
- 层次结构支持继承式派发
- 比协议更灵活但性能稍低
Clojure函数系列完成!接下来学习控制流!
最后更新:2026-03-27