Шаблоны проектирования для зависимых вычисляемых свойств в классе? - PullRequest
6 голосов
/ 27 октября 2010

У меня есть класс, представляющий сущность домена, которая содержит много вычисляемых свойств. Большинство расчетов зависит от других свойств, которые также рассчитываются. В простейшей форме пример класса может выглядеть примерно так:

public class AnalysisEntity
{
    public decimal InputA { get; set; }
    public decimal InputB { get; set; }
    public decimal InputC { get; set; }

    public decimal CalculatedValueA
    {
        get { return InputA * InputC; }
    }

    public decimal CalculatedValueB
    {
        get 
        {
            decimal factor = FactorGenerator.ExpensiveOperation();
            return CalculatedValueA / factor; 
        }
    }

    public decimal CalculatedValueC
    {
        get { return InputA * InputB; }
    }

    public decimal CalculatedValueD
    {
        get { return (CalculatedValueA * InputB) / CalculatedValueB; }
    }

    public decimal CalculatedValueE
    {
        get { return CalculatedValueD / aConstant; }
    }
}

Однако из-за этого решения у меня возникают следующие проблемы:

  1. Неэффективно, потому что некоторые вычисления (некоторые из которых являются длительными) вызывают многократно.
  2. Сложно провести единичное тестирование отдельных вычислений без предоставления всех необходимых входных данных для всех зависимых вычислений, чтобы они работали первыми.
  3. Трудно эффективно извлечь из постоянства (я использую NHibernate), потому что, хотя вычисленные данные могут быть сохранены в базе данных, они не извлекаются и вместо этого пересчитываются всякий раз, когда объект читается.
  4. Трудно добавлять вычисления, поскольку модульные тесты становятся все больше и больше с необходимыми входными данными.

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

Какие шаблоны проектирования и структуру классов я должен использовать для решения вышеуказанных проблем?

Спасибо

Ответы [ 4 ]

5 голосов
/ 27 октября 2010

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

Таким образом, когда записывается значение A, все вычисления, зависящие от A, переделываются.

Эта схема прекрасно работает, когда число операций чтения превышает количество операций записи.

1 голос
/ 01 ноября 2010

Кэширование является хорошей идеей - если оно может иметь большое количество попаданий в кэш.

Я бы не стал пересчитывать каждый раз при инициализации установщика, поскольку объект может еще не быть в согласованном состоянии (дляпример, что произойдет с новым объектом, если вы назначите InputA прежде, чем когда-либо установите остальные?)

Попытка выяснить зависимости между свойствами может быть довольно сложной и может быстро выйти из-под контроля, оставив ваш объект в непоследовательномгосударство.Я бы отделил ввод от вывода и имел явную операцию для расчета состояния.Например, пусть объект имеет свойства только для чтения.Затем создайте другой объект с входными данными и передайте его в качестве параметра методу «CalculateState».При желании, в зависимости от специфики, сделайте объект неизменным и передайте входные данные в конструктор.Наличие расчета в одном месте с четко определенным вводом поможет с тестированием.Вычисление курса может быть разложено, применены шаблоны проектирования и т. Д.

0 голосов
/ 27 октября 2010

Чтобы избежать повторных вычислений, вы можете следовать схеме вычисления любого значения, если связанные переменные изменились, это может быть достигнуто с помощью переменных состояния:

public class AnalysisEntity
{
    private decimal _ca;
    private decimal _cb;
    private decimal _cc;
    private bool calculate_a = false;
    private bool calculate_b = false;
    private bool calculate_c = false;
    private bool calculate_d = false;
    private bool calculate_e = false;

    public decimal InputA { get { return a;} set { a=value; calculate_a = true; calculate_c = true; } }
    public decimal InputB { get { return b;} set { b=value; calculate_c = true; calculate_d = true; } }
    public decimal InputC { get { return c;} set { c=value; calculate_a = true; } }

    public decimal CalculatedValueA
    {
        get 
        { 
            if( calculate_a ) { _ca = InputA * InputC; calculate_a = false; calculate_b = true; }
            return _ca; 
        }
    }

    public decimal CalculatedValueB
    {
        get 
        {
            if( calculate_b ) { _cb = (CalculatedValueA / FactorGenerator.ExpensiveOperation()); calculate_b = false; calculate_d = true; }
            return _cb;
        } 
    }

    public decimal CalculatedValueC
    {
        get 
        { 
            if( calculate_c ) { _cc = InputA * InputB; calculate_c = false; }
            return _cc;
        }
    }

    public decimal CalculatedValueD
    {
        get 
        { 
            if( calculate_d ) { _cd = (CalculatedValueA * InputB) / CalculatedValueB; calculate_d = false; calculate_e = true; }
            return _cd;     
        }
    }

    public decimal CalculatedValueE
    {
        get 
        { 
            if( calculate_e ) { _ce = CalculatedValueD / aConstant; calculate_e = false; }
            return _ce; 
        }
    }
}
0 голосов
/ 27 октября 2010

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

Конечно, это требует, чтобы вы знали зависимые свойства при изменении значения в вашем классе. Я хотел бы рассмотреть реализацию интерфейса INotifyPropertyChanged или IObservable<T> для тех свойств, от которых зависят другие, это будет шаблон проектирования Observer / Observable или Publish / Subscribe.

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