几乎每个程序都需要处理一些配置。这些配置可以有各种形式:
通过系统属性或者环境变量,呈简单的 x = y
通过配置文件,例如 .properties 文件,.ini 文件,.yml 文件,.edn 文件。
通过高大上的配置平台,例如 etcd, zookeeper 等。
但最影响程序设计的并非这些形式,而是如何在程序中使用这些数据。
最直接的想法是用全局变量:(def admin-user "admin")
,然后需要这些数据的部分可以通过全局变量来获取其内容。
这种方式的优点是方便,缺点则是过于自由的形式让配置处理变得分散到程序各处,对于“配置”这一本身有内聚性的需要没有恰当的一致处理。它还建立了随意性的依赖,例如任何使用上述 admin-user
的代码变为依赖于它,一旦我们改变它的数据类型或格式,会造成广泛的更改,而且这些更改非常容易引入 bug, 可以轻松成为维护的噩梦。
任何基于这种形式的配置库都共享同样的问题。例如 clojure 的 easy-conf 框架,基于 stuartsierra.component 库的配置框架的问题也一样。
理解配置问题的关键是,配置究竟是什么?它与系统内的其他数据比起来的关键不同是什么?
我对此的回答是,
配置在系统范围或session 范围或单次交互范围是只读的。这意味着我们不需要 var (变量)这样的灵活性。它也不像系统内的用户数据,是被用户请求来改变的。
配置是一种模板。它限制了系统的的具体行为。
因此,我们可以认为配置是制造具体逻辑的构造参数,我们仅仅在建造系统时需要它,而不需要在程序的其他生命期内关心它的形式。在 clojure 语言中,这意味着我们用它来构造函数即可。例如下面这个构造器用存在 map 中的 admin-user 配置数据来分类处理 req:
(defn mk-admin-wrapper [{:keys [admin-user]}]
(fn [req]
(let [username (get-in req [:params :username])]
(if (= username admin-user)
(handle-admin req)
(handle-normal req)))))
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。