光有Map是不够的
1 Map太多的后遗症
1.1 不记得这个Map里有啥Key了…
1.2 我刚才update的是哪一层来着??
2 阿波罗11
2.0.1 
2.1 src
{:date "1969-08-03T12:08:43.020" :name "阿波罗11号机组成员" :spacecraft "阿波罗11号" :destination "月球静海" :crew [{:name "阿姆斯特朗 Neil Alden Armstrong" :position "指令长"} {:name "奥尔德林 Buzz Aldrin" :position "登月舱驾驶员"} {:name "柯林斯 Michael Collins" :position "服务舱驾驶员"}]}
3 熟悉的味道
把所有宇航员的名字改成大写
(mapv (fn [mission] (update mission :crew (fn [crews] (mapv (fn [crew] (update crew :name string/upper-case)) crews)))) missions)
4 让我们分析下
这段代码里有:
- 2 个map
- 2 个update
- 3 个匿名function
+-------------------------+
| +---------------------+-----anonymous funcs-+
| | | |
(mapv (fn [mission]--------- map ------------------+
(update mission :crew | |
\ (fn [crews] | |
\ (mapv (fn [crew] -+--------------------+
\ (update crew :name string/upper-case))
\ crews)))) \
missions) \ \
\ \
\----- update -----\
5 现有处置手段
Specter 和 Spec 都是好工具
| 问题 | Spec | Specter |
|---|---|---|
| key 不出错 | OK | |
| 结构不用错 | OK | |
| 深度嵌套 | OK | |
| 长函数 |
仅仅有spec&specter是不够的
6 抽象
在正确的抽象层进行思考和编程
7 层级
8 例子: 哪怕最简单的重构也有好处
(require '[reagent.core :as r]) (def user-info (reagent/atom {})) (defn favorite-color-button [color] ;; reagent component [:button {:on-click (fn [] (swap! user-info assoc :favorite-color color)) :style {:background-color color}} color]) (defn favorite-color-panel [] [:div [:h2 "Favorite color"] [:h3 "Current: " (:favorite-color @user-info)] [:h3 "Change it by clicking below"] [favorite-color-button "green"] [favorite-color-button "blue"] [favorite-color-button "red"]]) (r/render-component [favorite-color-panel] js/klipse-container)
8.1 在libary层级
- key可能会写错, 修改map没有检查
- 可能会有多处
- 需要了解Map结构才能直到含义
(assoc user-info :favorite-color color)
^
|
|
|-+
|
clojure core libary|
8.2 在业务层
- 可读
- 一处改动,可能多处收益
- 调用函数有检查
domain
|
|
v
(defn set-favorite-color [user-info color]
(assoc user-info :favorite-color color))
^
|
|
|-+
|
clojure core libary|
9 big domain
实际上:我们最大量的代码在业务领域层
10 分层的domain
这些代码其实应该是有层次的,下面是应该的样子
11 重新组织
宇航员训练的一个例子, 与其一个大Map,我们可以抽取处3层实体结构
+--------------------------+---------+--------------------+
(assoc-in mission|[:crew :research-cosmonaut|:training|:centrifuge :status]|:pass)
| | | |
| | | |
| | | |
| Mission | person | training |
| | | |
| | | |
| | | |
+--------------------------+---------+--------------------+
11.1 code
(ns my-app.training) (defn set-status [record training status] (assoc record training status)) (ns my-app.person (:require [my-app.training :as training]) (defn set-training-status [person training status] (update person :training training/set-status training status)) (ns my-app.mission (:require [my-app.person :as person])) (defn set-training-status [mission position training status] (update-in mission [:crew position] person/set-training-status training status))
12 掌握平衡
13 分层的例子, 引用关系
- 上层只引用直接下层
- 下层 永远不要 引用上层
- 不跨层打交道
- 同层不打交道
变化
^
|
| +---------------------+
| | 满汉全席 |
| +----+---------------------+----+
| | 半成品(面团, 葱姜蒜末) |
| +----+-------------------------------+------+
| | 原材料(菜, 肉, 油, 辣椒) |
| +----+-------------+----------+------------------+----+
| | 加热 | | 切 |
| +------+------------------+----------+-----------------------+-----------+
| | 化学 (热, 酸, 蛋白, 淀粉)............... |
v +------------------------------------------------------------------------+
稳定