我正在编写一个财务计算类,它将有许多setter函数输入,一些私有中间值和一些getter函数作为输出。
私有中间值仅取决于输入值。
输出值(由公共getter访问)仅取决于输入和中间值。
最终你可以将整个事物描绘成一个有点纠结的非循环有向图,一边是一堆输入,最后流到右边的一堆输出。
实现此课程的最佳方法是什么。我有一些特殊要求:
在可能的情况下,懒惰评估。当输入发生变化时,我们现在可以知道可能需要哪些输出。
该类必须易于重新设计,因此某种声明性模型会被优先考虑。
理想情况下,我希望能够说C取决于A和B.如果在A或B改变之后请求C,那么它将知道需要重新计算C,否则C将永远不需要待刷新。
我有一个Java模式可以帮助我干净地实现这种计算器吗?
答案 0 :(得分:2)
你可以使用这样的模式。
double[] inputs = { ... }
double[] previousInputs = { Double.NaN, etc }; // NaN is never equal.
double[] outputs =
public void update() {
if (!Arrays.equals(inputs, previousInputs)) {
recalculate(inputs, outputs);
copyTo(inputs, previousInputs);
}
}
答案 1 :(得分:2)
好像你有某种实时流处理问题。
看一下twitter storm。即使您决定不使用它,您也可以借用tutorial page中解释的一些概念。
答案 2 :(得分:2)
您可以通过创建可重新计算的未来值来构建解决方案。
public class Computation<T> {
private T value;
private Set<Computation<?>> usedBy;
public T getValue(Computation<?> getter) {
if (usedBy == null) {
// value was not computed
value = compute();
usedBy = new HashSet();
}
if (getter != null) {
// add a dependency
usedBy.add(getter);
}
return value;
}
protected T compute() {
// override when needed a lazily-computed value
return null;
}
public void setValue(T value) {
// invalidate this value
invalidate();
// set the new value
this.value = value;
usedBy = new HashSet();
}
public void invalidate() {
if (usedBy != null) {
for (Computation<?> c : usedBy) {
c.invalidate();
}
usedBy = null;
}
value = null;
}
}
public class Business {
private Computation<Integer> a = new Computation<Integer>();
private Computation<Integer> b = new Computation<Integer>();
private Computation<Integer> c = new Computation<Integer>() {
public Integer compute() {
return a.getValue(this) + b.getValue(this);
}
};
public void setA(int v) {
a.setValue(v);
}
public void setB(int v) {
b.setValue(v);
}
public int getC() {
return c.getValue(null);
}
}
它完全是懒惰的并且计算出依赖性。
答案 3 :(得分:1)
就我个人而言,我同意彼得的观点,但为了论证,我还有另外两个答案。我建议查看规则引擎(例如Drools)以实现这样的灵活业务逻辑。它们的设计使得变量之间的更新规则易于设置和随意更改。它们也应该具有相当的性能。
然后,对于DYI-er,这是一个Spring灵感版本。最大的缺点是您将依赖项作为列表。您可以轻松使用HashMap,但之后您将失去语法安全性。
public abstract class Variable<T> {
private T currentValue;
private List<Variable<?>> dependencies = new ArrayList<Variable<?>>();
private List<Variable<?>> upstream = new ArrayList<Variable<?>>();
public T get() {
return currentValue;
}
public void set(T newValue) {
currentValue = newValue;
updateUpstream();
}
public abstract T recalculateValue(List<Variable<?>> dependencies);
private void update() {
set(recalculateValue());
}
private void updateUpstream() {
for(Variable<?> variable : upstream) {
variable.update();
}
}
private void addUpstream(Variable<?> variable) {
upstream.add(variable);
}
public void setDependencies(List<Variable<?>> dependencies) {
this.dependencies = dependencies;
for(Variable<?> variable) {
variable.addUpstream(this);
}
}
}
相应的applicationContext.xml如下所示:
<bean id="A" class="com.app.AVariable"/>
<bean id="B" class="com.app.BVariable"/>
<bean id="C" class="com.app.CVariable">
<property name="dependencies">
<list>
<ref bean="A"/>
<ref bean="B"/>
</list>
</property>
</bean>
为了获得额外的功劳,您可以实现bean后处理器,以根据注释自动计算和设置依赖项。例如:
public class CVariable extends Variable<Integer> {
private AVariable a;
private BVariable b;
@Dependency
public void setA(AVariable a) {
this.a = a;
}
@Dependency
public void setB(BVariable b) {
this.b = b;
}
//If you were going this route you wouldn't need the list of dependencies
public Integer recalculateValue() {
return a.get() + b.get();
}
}