前言

 在项目中我们一般会为实际问题域定义领域数据模型,譬如开发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/NodeListISeqable(-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/RegExpIFn(-invoke ([this s](re-matches this s))));; 使用
(#"s.*" "some") ;;=> some

2.使用extend-protocol

;; 扩展js/RegExp和js/String,让其可直接作为函数使用
(extend-protocol IFnjs/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](reifyIUser(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 aIUser(full-name [_] "Full Name")))(full-name a) ;;=>报错
(full-name b) ;;=>Full Name

specify!可为JS值追加指定的Protocol实现

(def a #js {})
(specify! aIUser(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/developerw...

尊重原创,转载请注明来自:http://www.cnblogs.com/fsjohn... ^_^肥仔John

(cljs/run-at (JSVM. :all) 一次说白DataType、Record和Protocol)相关推荐

  1. (cljs/run-at (JSVM. :all) 一次说白DataType、Record和Protocol) 1

    前言  在项目中我们一般会为实际问题域定义领域数据模型,譬如开发VDOM时自然而言就会定义个VNode数据类型,用于打包存储.操作相关数据.clj/cljs不单内置了List.Vector.Set和M ...

  2. Windows批量添加防火墙例外端口

    Windows下批量添加防火墙例外端口,查了网上资料,基本上都是使用"Netsh命令",循环增加端口,这会导致建立的规则特别多,不便于管理,查了下微软的资料,原来是Netsh命令, ...

  3. 深度窥探 QuickTest 视图(1)

    QuickTest 窗口包含众多元素,如关键字视图.专家视图.AS视图等.通过结合不同窗口的操作,可满足测试场景的需求. 1.1  QTP10.0窗口视图 1. 如图所示,启动QuickTest 10 ...

  4. 使用thrift进行跨语言调用(php c# java)

    1:前言 实际上本文说的是跨进程的异构语言调用,举个简单的例子就是利用PHP写的代码去调C#或是java写的服务端.其实除了本文提供的办法还有其他办法,例如http+xml(json)等等都能做到. ...

  5. 用YACC/LEX 设计计算机语言

    用YACC/LEX 设计计算机语言 前言: YACC (Yet Another Compiler Compiler) 是1974年在 Unix 下设计出来的一个优秀的计算机语法分析工具.LEX 是相应 ...

  6. 7.Spring Cloud Alibaba教程:整合Dubbo实现RPC调用

    概述 Apache Dubbo 是一款高性能的.基于Java的开源RPC框架,它提供了以下特性: 基于接口的远程方法调用 智能负载均衡 服务自动注册和发现 高可扩展性 运行期流量调度 可视化的服务治理 ...

  7. Tensorflow学习

    github地址:https://github.com/lawlite19/MachineLearning_TensorFlow 一.TensorFlow介绍 1.什么是TensorFlow 官网:h ...

  8. ui设计师常用的设计工具_2020年应该使用哪个UI设计工具?

    ui设计师常用的设计工具 重点 (Top highlight) It's 2020, the market today is saturated with UI design tools. Ever ...

  9. 闲来没事写个记事本玩玩!!!

    这两天工作压力大,还好今天见着太阳了,这会儿没事写了个记事本,功能单一,适合练手,可能对新手会有所帮助,贴上来看看吧, 说到实现 记事本,我们应该选择什么样的控件呢,TextBox,还是RichTex ...

最新文章

  1. ORACLE 配置文件
  2. python简单代码input-Python简单程序的练习
  3. SQL SERVER 执行计划各字段注释
  4. 基于Xml 的IOC 容器-获得配置路径
  5. tensorflow打印模型图_从Tensorflow模型文件中解析并显示网络结构图(pb模型篇)...
  6. leetcode22. 括号生成
  7. QML笔记-TextEdit的使用
  8. mpvue中使用小程序云开发总结
  9. java中怎么表示数组中的某个值_java中如何高效判断数组中是否包含某个特定的值...
  10. sqlserver查询语句实例
  11. iOS 新浪微博-1.1框架升级
  12. 谷歌开发者大会焦点:TensorFlow.js可制作微信小程序,Android 10原生支持5G,TF2.0大更新...
  13. python爬虫(五)---斗鱼主播图片下载并重命名
  14. 写一个块linux设备驱动
  15. UEStudio快捷键
  16. 【git版本控制】| git版本控制操作命令(全)
  17. 服务器 响应400,加载资源失败:服务器响应的状态为400:spring mvc
  18. 【U8】U8.11(8.12) access版本如何升级到用友T3及U8
  19. Android 解决RecyclerView瀑布流刷新之后Item位置改变
  20. 2021年美容师(初级)考试及美容师(初级)考试资料

热门文章

  1. Redhat系统下三种主要的软件包安装方法
  2. java与jquery的选择器区别_java day44【JQuery 基础:概念,快速入门,JQuery对象和JS对象区别与转换,选择器,DOM操作,案例】...
  3. mysql 只返回第一条_mybatis 关联查询时,从表只返回第一条记录解决办法
  4. java下拉菜单_薪资对比:Java开发和web前端薪资哪个好
  5. android内容提供者_挖穿Android第三十九天
  6. 虚拟机上部署的项目 访问路径怎么写_桌面虚拟化即将流行开来——基于Hyper-V虚拟机的桌面虚拟化部署...
  7. python3 json解析_Python3 JSON编码解码方法详解
  8. android contacts电话查询头像,android透过查询电话号码获取联系人头像
  9. html中dir标签的作用是什么意思,htmldir标签是干啥的?dir标签的具体定义和属性介绍...
  10. lambda 对象去重_最火的java8新特性:Lambda 表达式