2012-02-10 9 views
8

Estoy escribiendo una clase de cálculo financiero que tendrá una serie de entradas de función de configuración, algunos valores intermedios privados y un número de funciones getter como salidas.¿Cómo puedo implementar una clase con estado de evaluación diferida con dependencias internas en Java?

  • Los valores intermedios privados solo dependen de los valores de entrada.

  • Los valores de salida (accedidos por captadores públicos) solo dependen de las entradas y los valores intermedios.

En última instancia puede dibujar todo el asunto como un grafo acíclico dirigido un tanto enredado un poco con un montón de entradas en un lado, con el tiempo que fluye a un montón de salidas en el lado derecho.

¿Cuál es la mejor manera de implementar esta clase. Tengo algunos requisitos específicos:

  • Donde sea posible, lazy evaluate. Cuando un aporte cambia, ahora tenemos la forma de saber qué productos pueden requerirse.

  • La clase tiene que ser fácil de rediseñar, por lo que se preferiría algún tipo de modelo declarativo.

Idealmente me gustaría ser capaz de decir que C depende de A y B. Si C se solicitó después de A o B habían cambiado entonces sabría THT C necesita ser re-calculada, de lo contrario C nunca necesitaría actualizarse.

¿Hay algún patrón de Java que pueda ayudarme a implementar este tipo de calculadora?

+0

Algo así: Mantenga la entrada en un contenedor no específico; si se llama a un getter, cree un Strategy-Object basado en la entrada dada y los valores intermedios, y calcule el objeto SO como resultado. – ollins

Respuesta

2

Puede crear una solución creando un valor futuro que sea recalculable.

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

Es completamente vago y descubre las dependencias.

+0

¡Sí, esto es lo que estoy buscando! –

+0

Una pregunta acerca de la sintaxis aquí: en la declaración donde definimos el miembro de la clase c - esta afirmación parece definir un miembro y anular de forma simultánea un método en la clase - nunca antes había visto esta sintaxis. ¿Esta técnica tiene un nombre, y puede indicarme algo en la referencia del idioma donde puedo leer más? –

+1

Es una clase anónima. Más información: http://en.wikibooks.org/wiki/Java_Programming/Nested_Classes – Kru

2

Puede usar un patrón como este.

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

Sí, eso funciona y ya tengo algo similar (usando nulos en lugar de NaN), pero no es declarativo. Esa es una forma muy imperativa de hacerlo, ¿no crees? –

+0

Eso es verdad. A veces, el enfoque más simple es el mejor. Podrías hacerlo más declarativo, pero trataría de evitar hacerlo más complejo en el proceso. –

1

Personalmente, estoy de acuerdo con Peter, pero por razones de argumento tengo otras dos respuestas. Recomendaría consultar un motor de reglas (por ejemplo, Drools) para implementar una lógica empresarial flexible como esta. Están diseñados para que las reglas para las actualizaciones entre variables sean fáciles de configurar y cambiar a voluntad. También deberían ser bastante efectivos.

Luego, para el DYI-er, aquí hay una versión inspirada en la primavera. El mayor inconveniente es que obtiene sus dependencias como una lista. Podrías usar fácilmente un HashMap pero luego perderías la seguridad de tu sintaxis.

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

} 

El applicationContext.xml correspondiente se vería así:

<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> 

Para crédito adicional podría implementar un grano de post-procesador para calcular y establecer las dependencias automáticamente en función de las anotaciones. Por ejemplo:

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

} 
2

Parece que tiene algún tipo de problema de procesamiento de flujo en tiempo real.

Echa un vistazo a twitter storm. Incluso si decide no usarlo, puede tomar prestados algunos conceptos explicados en tutorial page.

+0

Esta tecnología es increíble, y si el proyecto se ampliara el tipo de cosas que me gustaría. ¿Esta tecnología es realmente utilizada por Twitter en algún lugar de su sistema? –

+1

Sí, actualmente se usa en twitter para mantener estadísticas sobre los clics de los usuarios para cada URL y dominio. Esta presentación de video http://www.infoq.com/presentations/Storm debería ser interesante para usted. –

Cuestiones relacionadas