Как я могу рефакторинг / расширить следующую модель - PullRequest
1 голос
/ 13 августа 2010

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

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.

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

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

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

internal interface IScenarioACalculations 
{
    float GetScenarioAValue5();
}

ScenarioACalculations : Calculations, IScenarioACalculations 
ScenarioACalculationsCache : CalculationsCache, IScenarioACalculations 

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

Извините, что так долго.

Спасибо.

Ответы [ 3 ]

3 голосов
/ 13 августа 2010

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

Попробуйте:

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 голос
/ 13 августа 2010

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

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

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

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

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

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

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

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

1 голос
/ 13 августа 2010

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

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

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

ScenarioACalculations : Calculations, IScenarioACalculations
ScenarioACalculationsCache : CalculationsCache, IScenarioACalculations 

Но теперь ваш IScenarioACalculations может быть брошен в ICalculationsAbstract

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