(cljs/run-at (JSVM. :all) "一次说白DataType、Record和Protocol")
前言
在项目中我们一般会为实际问题域定义领域数据模型,譬如开发VDOM时自然而言就会定义个VNode数据类型,用于打包存储、操作相关数据。clj/cljs不单内置了List、Vector、Set和Map等数据结构,还提供deftype和defrecord让我们可以自定义数据结构,以满足实际开发需求。
定义数据结构从Data Type和Record开始
提及数据结构很自然就想起C语言中的struct,结构中只有字段并没有定义任何方法,而这也是deftype和defrecord最基础的玩法。
示例
(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可以让我们实施面对接口编程。上面我们通过deftype和defrecord我们可以自定义数据结构,其实我们可以通过实现已有的Protocol或自定义的Protocol来扩展数据结构的能力。
deftype和defrecord在定义时实现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
specify和specify!为实例追加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,defrecord和defprotocol让我们从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
(cljs/run-at (JSVM. :all) "一次说白DataType、Record和Protocol")的更多相关文章
- iOS开发系列--Objective-C之协议、代码块、分类
概述 ObjC的语法主要基于smalltalk进行设计的,除了提供常规的面向对象特性外,还增加了很多其他特性,这一节将重点介绍ObjC中一些常用的语法特性.当然这些内容虽然和其他高级语言命名不一样,但 ...
- iOS-Objective-C基础
一.Foundation框架 概述 我们前面的章节中就一直新建Cocoa Class,那么Cocoa到底是什么,它和我们前面以及后面要讲的内容到底有什么关系呢?Objective-C开发中经常用到NS ...
- RichTextBoxEx2
using System;using System.Collections.Specialized;using System.Drawing;using System.Drawing.Imaging; ...
- RichTextBoxEx
using System; using System.Collections.Specialized; using System.Drawing; using System.Drawing.Imagi ...
- LR11
HP LoadRunner Readme for the Windows operating system Software version: 11.00 Publication date: Octo ...
- 【Hadoop代码笔记】通过JobClient对Jobtracker的调用详细了解Hadoop RPC
Hadoop的各个服务间,客户端和服务间的交互采用RPC方式.关于这种机制介绍的资源很多,也不难理解,这里不做背景介绍.只是尝试从Jobclient向JobTracker提交作业这个最简单的客户端服务 ...
- Qtp自动测试工具(案例学习)
♣Qtp是什么? ♣测试用例网站 ♦注册与登录 ♦测试脚本 ◊录制/执行测试脚本 ◊分析录制的测试脚本 ◊执行.查看测试脚本 ♦建立检查点 ...
- Windows批量添加防火墙例外端口
Windows下批量添加防火墙例外端口,查了网上资料,基本上都是使用"Netsh命令",循环增加端口,这会导致建立的规则特别多,不便于管理,查了下微软的资料,原来是Netsh命令, ...
- jenkin插件整理
分类 plugin名称 wiki地址 源码地址 plugin作用范围 备注 Build Reports构建报告(此类插件用来分析构建结果,比果代码检查,测试CASE分析,并将这些结果以报表,趋势图等形 ...
随机推荐
- HDFS运行原理
HDFS(Hadoop Distributed File System )Hadoop分布式文件系统.是根据google发表的论文翻版的.论文为GFS(Google File System)Googl ...
- 前端教你学UI——人物处理(一)
一.序言 本文作为本系列的第一篇写UI的文章,开头还是有必要申明一些东西的,本系列主要是为了作为博主在前端工作之余学习UI的一个记录,同时为了让更多的同行学习到一些编程之外的其他东西.所以本文会尽可能 ...
- VR全景智慧城市--2017年VR项目加盟将是一个机遇
全景智慧城市项目是河南艺境空间文化传播有限公司自主开发的国内第一家商业全景平台, 旨在构建全景城市,实现智慧生活,让人们随时随地身临其境拥有全世界,享受快捷.真实.趣味.优质生活. 以VR虚拟现实技术 ...
- setTimeout异步加载
两道经典的面试题,直接上代码 for(var i=0; i<3; i++){ setTimeout(function(){ i+=i; console.log(i); },1000) } var ...
- Java常用类之【字符串相关类型】
一.字符相关类型 分类: 1.不可变的字符序列: String类 2.可变的字符序列: StringBuilder类--->线程不安全的 执行效率相对较高 StringBuffer类---> ...
- Laravel项目修改时区
Laravel项目修改时区 最近做了一个支付宝支付的应用,现在还在开发过程中,今天早上起床之后先调试了一下项目,模拟支付了一笔(¥9999.00) 2333支付宝的沙箱环境啦,屌丝程序猿哪来这么多钱- ...
- 前端css要加的一些
编码格式 @charset "utf-8"; body的外边距设置 margin:0; 标签设置 form,ul,ol,li设置为padding:0; ul,ol,li设置为lis ...
- Spring 4学习——问题与注意事项(一)
1.Spring项目依赖的jar包有5个: 2.applicationContext.xml文件中,如下bean的property的name值对应的是HelloWorld类中的setter方法,即na ...
- [转] .NET领域驱动设计—初尝(疑问、模式、原则、工具、过程、框架、实践)
阅读目录: 1.1.疑问 1.1.1.UML何用 1.1.2.领域建模 1.2.模式 1.3.原则 1.5.过程 1.6.框架 1.7.项目演示 最近在研究DDD颇有收获,所以整理出来跟大家分享,共同 ...
- 将数据库从服务器移到浏览器--indexedDB基本操作封装
数据库是属于服务器的,这是天经地义的事,但是有时候数据也许并非需要存储在服务器,但是这些数据也是一条一条的记录,怎么办?今天来带领你领略一下H5的一个新特性--indexedDB的风骚.你会情不自禁的 ...