Как я могу реализовать ленивый класс с состоянием с внутренними зависимостями в Java? - PullRequest
8 голосов
/ 10 февраля 2012

Я пишу класс финансовых расчетов, который будет иметь несколько входных значений для функции-установщика, некоторые частные промежуточные значения и несколько функций-получателей в качестве выходных.

  • Частные промежуточные значениязависят только от входных значений.

  • Выходные значения (доступные для публичных получателей) зависят только от входных и промежуточных значений.

В конечном итоге вы могли бы нарисовать все это в виде несколько запутанного ациклического ориентированного графа с кучей входов на одной стороне, в конечном итоге перетекающих в кучу выходов на правой стороне.

Какой лучший способ реализовать этот класс.У меня есть некоторые особые требования:

  • Где это возможно, ленивый оценить.Когда входные данные меняются, у нас теперь есть способ узнать, какие выходные данные могут потребоваться.

  • Класс должен быть легко перепроектирован, поэтому предпочтительнее использовать некоторую декларативную модель.

В идеале я хотел бы сказать, что C зависит от A и B. Если C запрашивался после изменения A или B, он знал бы, что C необходимопересчитать, иначе C никогда не потребуется обновлять.

Есть ли шаблон Java, который может помочь мне безошибочно реализовать калькулятор такого типа?

Ответы [ 4 ]

2 голосов
/ 11 февраля 2012

Вы можете построить решение, создав будущее значение, которое можно пересчитать.

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);
  }
}

Это просто лень и вычисляет зависимости.

2 голосов
/ 10 февраля 2012

Похоже, у вас какая-то проблема с обработкой потока в реальном времени.

Взгляните на твиттер шторм . Даже если вы решите не использовать его, вы можете позаимствовать некоторые концепции, объясненные на странице учебника .

2 голосов
/ 10 февраля 2012

Вы можете использовать шаблон как этот.

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 голос
/ 10 февраля 2012

Лично я согласен с Питером, но ради аргументов у меня есть два других ответа. Я бы рекомендовал взглянуть на механизм правил (например, 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>

Для дополнительного кредита вы можете реализовать постпроцессор бина для автоматического расчета и установки зависимостей на основе аннотаций. Например:

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();
  }

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...