Я все еще вижу проблему, когда мне всегда нужно все модифицировать, чтобы добавить больше вычислений. Возможно, у вашего 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, если свойства были доступны для записи, вы не хотите изменять параметры для кэшированного сценария и отменять проверку сохраненного значения.