几乎每个程序都需要处理一些配置。这些配置可以有各种形式:

  • 通过系统属性或者环境变量,呈简单的 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)))))

robertluo
738 声望21 粉丝

引用和评论

0 条评论