2010-08-13 2 views
1

В настоящее время у меня есть реализация кеша (с использованием массивов) для тяжелых вычислений, выполненных во время моделирования. Структура кэша выглядит следующим образом:Как я могу реорганизовать/расширить следующую модель

alt text

Как это работает:

CalculationsAbstract calculationsCache = new CalculationsCache(); 

// Constructor of CalculationsCache 
public CalculationsCache() 
{ 
    this.Proxy = new Calculations(); 
    Proxy.Proxy = this; 
} 

calculationsCache.CalculateValue1();  
// Checks "Value1" array for existing value, if not, the actual computation is called 
// via Proxy object, value retrieved is cached in array then returned to user. 

Теперь я пытаюсь добавить новые вычисления, которые являются специфическими для определенного сценария, и Wouldn чтобы их помещали в CalculationsAbstract, Calculations и CalculationsCache, однако ScenarioA все равно будет использовать существующие вычисления в старых классах.

Я пытаюсь добавить новые вычисления и их массивы в новые классы с именем ScenarioACalculations и ScenarioACalculationsCache, как это было сделано для Value1, Value2, ... и т. Д., Но я смущен относительно того, как эти новые классы будут вписываются в существующую модель.

Это то, что я пытался сделать:

internal interface IScenarioACalculations 
{ 
    float GetScenarioAValue5(); 
} 

ScenarioACalculations : Calculations, IScenarioACalculations 
ScenarioACalculationsCache : CalculationsCache, IScenarioACalculations 

Учитывая, что на протяжении всего моего проекта, я только держать ссылку на тип CalculationsAbstract (как в примере кода выше), и я не могу бросить свою IScenarioACalculations object to CalculationsAbstract, что было бы лучшим способом добавить вычисления ScenarioA и, возможно, ScenarioB ... и т. д. в будущем?

Прошу прощения за это.

спасибо.

ответ

3

В принципе, у вас есть Decorator в CalculationsCache. Поскольку писать такой тонкий слой вокруг объекта Calculations будет проблемой обслуживания, вы должны принести кеширование внутри самого объекта Calculations.

Попробуйте это:

Scenario 
{ 
    IDictionary<int, float> cache = new Dictionary<int, float>; 

    void Reset() 
    { 
    cache.Clear(); 
    } 
} 

PhysicsScenario : Scenario 
{ 
    const int AccelerationType = 1; // or string, or whatever. Just needs to be a key into the cache. 
    float[] CalculateAcceleration() 
    { 
    if (cache.Keys.Contains(AccelerationType)) 
    { 
     return cache[AccelerationType]; 
    } 
    else 
    { 
     float[] values = ActualCalculateAcceleration(); 
     cache.Add(AccelerationType, values); 
     return values; 
    } 
    } 
    float[] CalculateVelocity() {...} 
    // More heavy calculations 
} 

ChemistryScenario : Scenario 
{ 
    float[] CalculateVolume() {...} 
    float[] CalculateCalorificValue() {...} 
    // More heavy calculations 
} 

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

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

1

Похоже, вы описали эту модель, что два метода в CalculationsAbstract всегда переопределены. Почему бы не сделать его интерфейс ICalculationsAbstract и имеют

internal interface IScenarioACalculations : ICalculationsAbstract 
{ 
    float GetScenarioAValue5(); 
} 

Вы все еще можете иметь:

ScenarioACalculations : Calculations, IScenarioACalculations 
ScenarioACalculationsCache : CalculationsCache, IScenarioACalculations 

Но теперь ваши IScenarioACalculations могут быть отлиты в ICalculationsAbstract

1

Я все еще вижу проблему всегда иметь изменить все, чтобы добавить дополнительные вычисления. Возможно, ваш ICalculations должен иметь один метод Calculate(), который принимал IScenario. IScenario предоставит метод для выполнения сценария и возврата, метод проверки равенства, получения хэш-кода и, возможно, метода клонирования.

Идея состоит в том, что каждый новый расчет, который вы хотите добавить, представляет собой отдельный класс. Каждый из них может быть создан и передан в ICalculations, который затем может решить, что с ним делать. Скорее всего, реализация проверила бы хэш-код IScenario's на кеш-файл какого-либо типа, если он найден, возвратит значение, если не вызывает IScenario's Execute(), сохраните значение с помощью хэш-кода IScenario's для кэширования, а затем верните его.

Таким образом, это будет в основном Command Pattern. Я вижу, что вы получаете то, что реализация ICalculations не должна будет меняться очень часто (при условии, что я понимаю, что вы делаете), и есть легкая точка расширения. Вы могли бы также позже добавить идеи какого-то контекста запуска и создать компоновки сценариев, чтобы позволить такие вещи, как распределенные вычисления, которые агрегируются, но для пользователя классов/интерфейсов очень мало, придется менять, в то время как вы можете убрать эти сложности в классы, которые в них нуждаются.

Недостатки, которые я вижу, это то, что вам нужно знать, чтобы переопределить метод GetHashCode() и Equals() для каждого сценария. Возможно, это можно сделать общим способом (это включает в себя Clone()) с использованием отражения, если вы знаете, что вам нужны только публичные свойства, которые всегда будут представлять параметры для расчета, например. Также я знаю, что ReSharper может генерировать эти методы для вас.

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

Просто некоторый основной код в качестве примера:

interface ICalculations { 
    double Calculate(IScenario scenario); 
} 

interface IScenario{ 
    double Execute(); 
    IScenario Clone(); 
} 

public class CalculationsCacher: ICalculations { 
    IDictionary<IScenario, double> _cache; 

    public CalculationsCacher(IDictionary<IScenario, double> existingCache = new Dictionary<IScenario, double>()){ 
     //c#4 has optional parameters 
     _cache = existingCache 
    } 

    public double Calculate(IScenario scenario){ 
     if(_cache.ContainsKey[scenario]) return _cache[scenario]; 

     _cache[scenario.Clone()] = scenario.Execute(); 
     return _cache[scenario]; 
    } 
} 

class AccelerationScenario: IScenario{ 
    // properties 
    public AccelerationScenario(double distance, TimeSpan time){ 
     // set things 
    } 

    public double Execute(){ 
     //calculate 
    } 

    public IScenario Clone(){ 
     //use contructor to build a new one 
    } 

    public override int GetHashCode(){ 
     //generate the hashcode 
    } 

    public override bool Equals(object obj){ 
     //you should also override this when you override GetHashCode() 
    } 
} 

//somewhere in your app 
var scenario = new AccerlationScenario(/* values */); 
return calculations.Calculate(scenario); 

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

Смежные вопросы