I'm dealing with a modeling system (financial) that has dozens of variables. Some of the variables are independent, and function as inputs to the system; most of them are calculated from other variables (independent and calculated) in the system.
What I'm looking for is a clean, elegant way to:
define the function of each dependent variable in the system
trigger a re-calculation, whenever a variable changes, of the variables that depend on it
A naive way to do this would be to write a single class that implements INotifyPropertyChanged, and uses a massive case statement that lists out all the variable names x1, x2, ... xn on which others depend, and, whenever a variable xi changes, triggers a recalculation of each of that variable's dependencies.
I feel that this naive approach is flawed, and that there must be a cleaner way. I started down the path of defining a CalculationManager<TModel> class, which would be used (in a simple example) something like as follows:
public class Model : INotifyPropertyChanged
{
private CalculationManager<Model> _calculationManager = new CalculationManager<Model>();
// each setter triggers a "PropertyChanged" event
public double? Height { get; set; }
public double? Weight { get; set; }
public double? BMI { get; set; }
public Model()
{
_calculationManager.DefineDependency<double?>(
forProperty: model => model.BMI,
usingCalculation: (height, weight) => weight / Math.Pow(height, 2),
withInputs: model => model.Height, model.Weight);
}
// INotifyPropertyChanged implementation here
}
I won't reproduce CalculationManager<TModel> here, but the basic idea is that it sets up a dependency map, listens for PropertyChanged events, and updates dependent properties as needed. I still feel that I'm missing something major here, and that this isn't the right approach:
the (mis)use of INotifyPropertyChanged seems to me like a code smell
the withInputs parameter is defined as params Expression<Func<TModel, T>>[] args, which means that the argument list of usingCalculation is not checked at compile time
the argument list (weight, height) is redundantly defined in both usingCalculation and withInputs
I am sure that this kind of system of dependent variables must be common in computational mathematics, physics, finance, and other fields. Does someone know of an established set of ideas that deal with what I'm grasping at here? Would this be a suitable application for a functional language like F#?
Edit
More context:
The model currently exists in an Excel spreadsheet, and is being migrated to a C# application. It is run on-demand, and the variables can be modified by the user from the application's UI. Its purpose is to retrieve variables that the business is interested in, given current inputs from the markets, and model parameters set by the business.