光有Map是不够的

1 Map太多的后遗症

1.1 不记得这个Map里有啥Key了…

1.2 我刚才update的是哪一层来着??

2 阿波罗11

2.0.1 moon.jpg

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 抽象

在正确的抽象层进行思考和编程

abs.png

7 层级

layers.png

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

实际上:我们最大量的代码在业务领域层

big-domain.png

10 分层的domain

这些代码其实应该是有层次的,下面是应该的样子

layered-domain.png

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 掌握平衡

balance.png

13 分层的例子, 引用关系

  • 上层只引用直接下层
  • 下层 永远不要 引用上层
  • 不跨层打交道
  • 同层不打交道
           变化
            ^
            |
            |                              +---------------------+
            |                              |      满汉全席       |
            |                         +----+---------------------+----+
            |                         | 半成品(面团, 葱姜蒜末)       |
            |                    +----+-------------------------------+------+
            |                    |          原材料(菜, 肉, 油, 辣椒)        |
            |               +----+-------------+----------+------------------+----+
            |               |       加热       |          |   切                  |
            |        +------+------------------+----------+-----------------------+-----------+
            |        |            化学     (热, 酸, 蛋白, 淀粉)...............                |
            v        +------------------------------------------------------------------------+
           稳定

Author: Kevin Li

Created: 2020-01-09 Thu 09:17