當前位置:
首頁 > 知識 > "一次說白DataType、Record和Protocol")

"一次說白DataType、Record和Protocol")


前言

在項目中我們一般會為實際問題域定義領域數據模型,譬如開發VDOM時自然而言就會定義個VNode數據類型,用於打包存儲、操作相關數據。clj/cljs不單內置了ListVectorSetMap等數據結構,還提供deftypedefrecord讓我們可以自定義數據結構,以滿足實際開發需求。

定義數據結構從Data Type和Record開始

提及數據結構很自然就想起C語言中的struct,結構中只有欄位並沒有定義任何方法,而這也是deftypedefrecord最基礎的玩法。

示例

(deftype VNode1 [tag props])
(defrecord VNode2 [tag props])

(def vnode1
(VNode1. "DIV" {:textContent "Hello world!"}))
;; 或 (->VNode1 "DIV" {:textContent "Hello world!"})

(def vnode2
(VNode2. "DIV" {:textContent "Hello world!"}))
;; 或 (->VNode2 "DIV" {:textContent "Hello world!"})
;; 或 (map->VNode2 {:tag "DIV", :props {:textContent "Hello world!"}})

這樣一看兩者貌似沒啥區別,其實區別在於成員的操作上

;; deftype取成員值
(.-tag vnode1) ;;=> DIV
;; defrecord取成員值
(:tag vnode2) ;;=> DIV

;; deftype修改成員值
(set! (.-tag vnode1) "SPAN")
;; 或 (aset vnode1 "tag" "SPAN")
(.-tag vnode1) ;;=> SPAN

;; defrecord無法修改值,只能產生一個新實例
(def vnode3
(assoc vnode2 :tag "SPAN"))
(:tag vnode2) ;;=> DIV
(:tag vnode3) ;;=> SPAN

從上面我們可以看到defrecord定義的數據結構可以視作Map來操作,而deftype則不能。

但上述均為術,而背後的道則是:

在OOP中我們會建立兩類數據模型:1.編程領域模型;2.應用領域模型。對於編程領域模型(如String等),我們可以採用deftype來定義,從而提供特殊化能力;但對於應用領域模型而言,我們應該對其進行抽象,從而採用已有的工具(如assoc,filter等)對其進行加工,並且對於應用領域模型而言,一切屬性應該均是可被訪問的,並不存在私有的需要,因為一切屬性均為不可變的哦。

Protocol

Protocol如同Interface可以讓我們實施面對介面編程。上面我們通過deftypedefrecord我們可以自定義數據結構,其實我們可以通過實現已有的Protocol或自定義的Protocol來擴展數據結構的能力。

deftypedefrecord在定義時實現Protocol

;; 定義protocol IA
(defprotocol IA
(println [this])
(log [this msg]))

;; 定義protocol IB
(defprotocol IB
(print [this]
[this msg]))

;; 定義數據結構VNode並實現IA和IB
(defrecord VNode [tag props]
IA
(println [this]
(println (:tag this)))
(log [this msg]
(println msg ":" (:tag this)))
IB
(print ([this]
(print (:tag this)))))

;; 各種調用
(def vnode (VNode. "DIV" {:textContent "Hello!"}))
(println vnode)
(log vnode "Oh-yeah:")
(print vnode)

注意IB中定義print為Multi-arity method,因此實現中即使是僅僅實現其中一個函數簽名,也要以Multi-arity method的方式實現。

(print ([this] (print (:tag this))))

否則會報java.lang.UnsupportedOperationException: nth not supported on this type: Symbol的異常

對已有的數據結構追加實現Protocol

Protocol強大之處就是我們可以在運行時擴展已有數據結構的行為,其中可通過extend-type對某個數據結構實現多個Protocol,通過extend-protocol對多個數據結構實現指定Protocol。

1.使用extend-type

;; 擴展js/NodeList,讓其可轉換為seq
(extend-type js/NodeList
ISeqable
(-seq [this]
(let [l (.-length this)
v (transient [])]
(doseq [i (range l)]
(->> i
(aget this)
(conj! v)))
(persistent! v))))
;; 使用
(map
#(.-textContent %)
(js/document.querySelector "div"))

;; 擴展js/RegExp,讓其可直接作為函數使用
(extend-type js/RegExp
IFn
(-invoke ([this s]
(re-matches this s))))

;; 使用
(#"s.*" "some") ;;=> some

2.使用extend-protocol

;; 擴展js/RegExp和js/String,讓其可直接作為函數使用
(extend-protocol IFn
js/RegExp
(-invoke ([this s] (re-matches this s)))
js/String
(-invoke ([this n] (clojure.string/join (take n this)))))

;; 使用
(#"s.*" "some") ;;=> some
("test" 2) ;;=> "te"

另外我們可以通過satisfies?來檢查某數據類型實例是否實現指定的Protocol

(satisfies? IFn #"test") ;;=> true
;;對於IFn我們可以直接調用Ifn?
(Ifn? #"test") ;;=>true

reify構造實現指定Protocol的無屬性實例

(defn user
[firstname lastname]
(reify
IUser
(full-name [_] (str firstname lastname))))
;; 使用
(def me (user "john" "Huang"))
(full-name me) ;;=> johnHuang

specifyspecify!為實例追加Protocol實現

specify可為不可變(immutable)和可複製(copyable,實現了ICloneable)的值,追加指定的Protocol實現。其實就是向cljs的值追加啦!

(def a "johnHuang")
(def b (specify a
IUser
(full-name [_] "Full Name")))

(full-name a) ;;=>報錯
(full-name b) ;;=>Full Name

specify!可為JS值追加指定的Protocol實現

(def a #js {})
(specify! a
IUser
(full-name [_] "Full Name"))

(full-name a) ;;=> "Full Name"

總結

cljs建議對數據結構進行抽象,因此除了List,Map,Set,Vector外還提供了Seq;並內置一系列數據操作的函數,如map,filter,reduce等。而deftype、defrecord更多是針對面向對象編程來使用,或者是面對內置操作不足以描述邏輯時作為擴展的手段。也正是deftype,defrecorddefprotocol讓我們從OOP轉FP時感覺更加舒坦一點。

另外deftype,defrecord和protocol這套還有效地解決Expression Problem,具體請查看http://www.ibm.com/developerworks/library/j-clojure-protocols/

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 科技優家 的精彩文章:

TAG:科技優家 |

您可能感興趣

如果說白送你一台手機,只能從iPhone8和OPPOFind X里選!你選那個?
別說白Tee無聊!它的這10種玩法,你都get了嗎?
型男Tips:誰說白T最好搭?這三點沒注意,也會很NG
GO、QG打法深度剖析,解說白樂一語中的:GO4:1戰勝YTK,勝出理由很簡單
dopa直播談S8:對線強就是一切,說白了抗壓其實就是菜
每日穿搭新look//誰說白色不好駕馭?我看你能行!
魅族pro7誰之過,黃章說白永祥!
話說白tee怎麼這麼讓女星偏愛,隨性百搭是重點!
聽說白敬亭童顏巨臂,而你還沒get夏天的馬甲線!
G?潮 誰說白Tee+牛仔褲沒有配搭技巧可言?時尚達人是這樣穿的!
《別再說白敬亭是「注孤生」了,這兩人才是cp!》
誰說白Tee搭牛仔褲沒有配搭技巧可言?時尚達人是這樣穿的!
G5欲大勝?泰倫盧:騎士有3大優勢,網友:說白了就一個優勢!
美妖課堂NO.16:誰說白色單品不適合冬天穿,這樣搭,氣質都變好了!
CF手游:天美說白羊座適合打生化,這槍算什麼?我快皮斷腿了!
誰說白T恤爛大街,那是你不會搭!
直男最受不了4種妝容:貼雙眼皮他會撕,塗BB他說白,你中幾個
聖鬥士:撒卡修為什麼用AE?說白了,單人根本破不了天舞寶輪!
揭秘:1948年淮海戰役,為何說白崇禧錯過一次創造奇蹟的機會
動畫,說白了先是一條產業鏈