前言

 在项目中我们一般会为实际问题域定义领域数据模型,譬如开发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"),这种方式不会改变vnode1的值
(.-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/developerworks/library/j-clojure-protocols/

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

如果您觉得本文的内容有趣就扫一下吧!捐赠互勉!

本文转自^_^肥仔John博客园博客,原文链接:http://www.cnblogs.com/fsjohnhuang/p/7154085.html,如需转载请自行联系原作者

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

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

    前言  在项目中我们一般会为实际问题域定义领域数据模型,譬如开发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+rac+算法,Oracle RAC中的投票算法
  2. 给Anaconda安装国内镜像,加快下载速度
  3. 77. 组合(回溯算法)
  4. 使用清华开源镜像安装tensorflow
  5. javascript中本地储存、离线缓存、地理定位、网络状态
  6. 尚学堂java 参考答案 第七章
  7. mac服务器证书失效,Mac OS X Server:软件更新证书过期
  8. 鸿蒙app迁移,余承东宣布:明年3月P40首发鸿蒙系统!主流App将迁移鸿蒙
  9. zabbix 监控mongodb
  10. Hive MapJoin OOM
  11. TensorFlow学习笔记——TensorFlow入门
  12. 软考中级网络工程师真题资料
  13. win10 如何启用虚拟化 Hyper-V
  14. MFC应用程序无法正常启动(0x0150002)。请单击“确定”关闭应用程序
  15. 原码、反码、补码、移码 基本介绍
  16. 吴恩达机器学习系列课程笔记——第十一章:机器学习系统的设计(Machine Learning System Design)
  17. 学习OpenMV(一)详细参数及简单介绍
  18. 豆瓣电影flask网页
  19. 正确设置Cisco路由器时间和时区
  20. 通过Python的speech_recognition库将音频文件转为文字

热门文章

  1. SCPPO(二十八):通过JS实现自动刷新进度
  2. 这个机器人不学数据集,“纯玩”get各类家务技能,LeCun觉得很赞
  3. Facebook的AI视频聊天设备,终于要发售了
  4. 脑子瓦特?记忆力受损?试试AI调控的闭环电击颞叶疗法
  5. ubuntu18.04server 真机无法自动获取IP解决方法
  6. hihoCoder1223 不等式
  7. 关于Cocos2d-x的粒子系统
  8. hibernate缓存理解
  9. 如何实现wpf的多国语言
  10. Java 8新特性探究(五)重复注解(repeating annotations)