首页 > 解决方案 > 当另一个字段更新时更新一个字段的模式的标准实践

问题描述

假设您有两个值x,并且每次更新时都应该计算y它们。在 Java 中,一切都是对象,所以我别无选择。这样做的方法是通过一个类,例如yx

public Data {
   private type x, y;

   Data(type x) {
      this.x = x;
      this.y = null;
   }
   
   void updateX(type2 val) {
      // perform operation to update X
      // perform (expensive) operation to update Y
   }
   
   void getX() {...}
   void getY() {...}
}

在 clojure 中,直接翻译可能使用deftypeand defprotocol/ definterface。但这对于仅仅定义两个变量之间的依赖关系来说太过分了。特别是,我反对给它命名并将其与实际类型和协议同等对待。

我知道这就是defrecords 的用途——当您需要创建一个不代表业务领域中的实体的类,但defrecords 是不可变的。

我还有其他选择吗?我看了看,RxClojure因为这里的情况似乎是“反应性的”,但这似乎是面向“发射”事件,而不是例如将最新的事件存储在原子中。

标签: clojure

解决方案


我不禁认为你想多了。

(def some-var (atom some-value))

(def derived (atom (some-expensive-fn some-var)))

(defn update-derived
  [old-state new-state]
  (new-state)

(defn update-var
  [x]
  (swap! derived 
         update-derived 
         (swap! some-var (fn [old-state] (x)))))

我们定义了一些状态,一些派生状态,以及一个接受新值并更新两者的函数。

根据评论编辑

如果您希望能够多次执行此操作,您可以使用返回闭包的工厂:

(defn make-stateful-thing-with-derived-value
  [init-x-value compute-y]
  (let [x (atom init-x-value)
        y (atom (compute-y init-x-value))]
    (list x
          y
          (fn [new-x-value]
            (swap! y
                   (fn [old-s] (compute-y new-x-value))
                   (swap! x (fn [old-s] (new-x-value)))))))

该函数接受一个初始 x 值和一个派生 y 的函数,并返回一个包含原子 x 和 y 的列表以及一个接受值以更新它们的函数。


推荐阅读