协议与记录 #
一、协议简介 #
协议(Protocol)是Clojure的高性能多态机制,类似于接口但更灵活。协议支持对现有类型进行扩展。
1.1 协议vs多重方法 #
| 特性 | 协议 | 多重方法 |
|---|---|---|
| 派发方式 | 类型 | 任意函数 |
| 性能 | 高 | 中 |
| 扩展性 | 可扩展现有类型 | 灵活 |
| 适用场景 | 类型多态 | 复杂派发 |
1.2 定义协议 #
clojure
(defprotocol Drawable
(draw [this])
(area [this]))
(defprotocol Named
(name [this])
(full-name [this]))
二、记录(Record) #
2.1 定义记录 #
clojure
(defrecord Circle [radius])
(def circle (->Circle 5))
(:radius circle)
(.radius circle)
2.2 实现协议 #
clojure
(defrecord Circle [radius]
Drawable
(draw [this]
(str "Drawing circle with radius " radius))
(area [this]
(* Math/PI radius radius)))
(def c (->Circle 10))
(draw c)
(area c)
2.3 创建记录实例 #
clojure
(def c1 (->Circle 5))
(def c2 (Circle. 5))
(def c3 (map->Circle {:radius 5}))
(into {} c1)
2.4 多协议实现 #
clojure
(defrecord Rectangle [width height]
Drawable
(draw [this]
(str "Drawing rectangle " width "x" height))
(area [this]
(* width height))
Named
(name [this]
"rectangle")
(full-name [this]
(str width "x" height " rectangle")))
(def r (->Rectangle 10 5))
(draw r)
(area r)
(name r)
三、扩展协议 #
3.1 extend-type #
为现有类型扩展协议:
clojure
(extend-type String
Named
(name [this]
this)
(full-name [this]
(str "String: " this)))
(name "hello")
(full-name "hello")
3.2 extend-protocol #
一次为多个类型扩展协议:
clojure
(extend-protocol Named
String
(name [this] this)
(full-name [this] (str "String: " this))
Number
(name [this] (str "Number: " this))
(full-name [this] (str "Number: " this)))
(name "test")
(name 42)
3.3 扩展Java类型 #
clojure
(extend-type java.util.Collection
Drawable
(draw [this]
(str "Collection with " (count this) " items")))
(draw [1 2 3])
四、类型(deftype) #
4.1 定义类型 #
clojure
(deftype Point [x y])
(def p (->Point 10 20))
(.x p)
(.y p)
4.2 type vs record #
| 特性 | deftype | defrecord |
|---|---|---|
| 字段访问 | 直接 | 通过关键字 |
| Map接口 | 不支持 | 支持 |
| 元数据 | 不支持 | 支持 |
| 性能 | 更高 | 高 |
| 用途 | 底层实现 | 数据载体 |
4.3 实现协议 #
clojure
(deftype Vector2D [x y]
Drawable
(draw [this]
(str "Vector(" x ", " y ")"))
(area [this]
(* x y)))
(def v (->Vector2D 3 4))
(draw v)
(area v)
4.4 可变字段 #
clojure
(deftype Counter [^:volatile-mutable count]
Drawable
(draw [this]
(str "Counter: " count))
(area [this]
(set! count (inc count))
count))
(def counter (->Counter 0))
(draw counter)
(area counter)
(area counter)
五、协议函数 #
5.1 可选参数 #
clojure
(defprotocol Formatter
(format [this] [this options]))
(defrecord SimpleFormatter [data]
Formatter
(format [this]
(format this {}))
(format [this options]
(str data " with " options)))
(def f (->SimpleFormatter "data"))
(format f)
(format f {:style :pretty})
5.2 协议继承 #
clojure
(defprotocol Shape
(perimeter [this]))
(defrecord Triangle [a b c]
Shape
(perimeter [this]
(+ a b c)))
(def t (->Triangle 3 4 5))
(perimeter t)
六、实践示例 #
6.1 图形系统 #
clojure
(defprotocol Shape
(area [this])
(perimeter [this])
(scale [this factor]))
(defrecord Circle [radius]
Shape
(area [this]
(* Math/PI radius radius))
(perimeter [this]
(* 2 Math/PI radius))
(scale [this factor]
(->Circle (* radius factor))))
(defrecord Rectangle [width height]
Shape
(area [this]
(* width height))
(perimeter [this]
(* 2 (+ width height)))
(scale [this factor]
(->Rectangle (* width factor) (* height factor))))
(def shapes [(->Circle 5) (->Rectangle 10 5)])
(map area shapes)
(map perimeter shapes)
6.2 数据库访问 #
clojure
(defprotocol Database
(connect [this])
(disconnect [this])
(query [this sql]))
(defrecord PostgresDB [host port db-name]
Database
(connect [this]
(str "Connecting to Postgres: " host ":" port "/" db-name))
(disconnect [this]
(str "Disconnecting from Postgres"))
(query [this sql]
(str "Executing: " sql)))
(defrecord MySQLDB [host port db-name]
Database
(connect [this]
(str "Connecting to MySQL: " host ":" port "/" db-name))
(disconnect [this]
(str "Disconnecting from MySQL"))
(query [this sql]
(str "Executing: " sql)))
(defn use-database [db]
(connect db)
(query db "SELECT * FROM users")
(disconnect db))
(use-database (->PostgresDB "localhost" 5432 "mydb"))
6.3 序列化系统 #
clojure
(defprotocol Serializable
(serialize [this])
(deserialize [this data]))
(defrecord JSONSerializer []
Serializable
(serialize [this]
:json)
(deserialize [this data]
(str "JSON: " data)))
(defrecord XMLSerializer []
Serializable
(serialize [this]
:xml)
(deserialize [this data]
(str "XML: " data)))
(defn process [serializer data]
(deserialize serializer data))
(process (->JSONSerializer) {:a 1})
七、记录作为Map #
7.1 Map接口 #
clojure
(defrecord User [name email])
(def user (->User "Alice" "alice@example.com"))
(:name user)
(assoc user :age 30)
(dissoc user :email)
(contains? user :name)
(keys user)
(vals user)
7.2 转换 #
clojure
(def user (->User "Alice" "alice@example.com"))
(into {} user)
(-> user
(assoc :age 30)
(update :name str " Smith"))
八、性能考虑 #
8.1 协议调用性能 #
clojure
(defprotocol FastProtocol
(fast-method [this]))
(defrecord FastRecord [data]
FastProtocol
(fast-method [this] data))
(defrecord SlowRecord [data])
(extend-type SlowRecord
FastProtocol
(fast-method [this] (:data this)))
(def fast (->FastRecord "test"))
(def slow (->SlowRecord "test"))
(time (dotimes [_ 1000000] (fast-method fast)))
(time (dotimes [_ 1000000] (fast-method slow)))
8.2 类型提示 #
clojure
(defn process-shape [^Shape shape]
(area shape))
九、总结 #
协议与记录速查:
| 概念 | 用途 |
|---|---|
defprotocol |
定义协议 |
defrecord |
定义记录 |
deftype |
定义类型 |
extend-type |
扩展单个类型 |
extend-protocol |
扩展多个类型 |
关键点:
- 协议提供高性能多态
- 记录是数据载体,支持Map接口
- 类型适合底层实现
- 可以扩展现有类型
- 记录可以像Map一样操作
下一步,让我们学习元数据!
最后更新:2026-03-27