我目前有一个缓存实现(使用数组)用于在模拟期间执行的繁重计算。缓存的结构如下:
它的工作方式:
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等的最佳方法?
很抱歉这么长。
谢谢。
答案 0 :(得分:3)
基本上,你在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对象完全相同,您可以使用它。
如果还有其他内容,您可以使用Inversion of Control原则从容器中传递缓存Dictionary。
答案 1 :(得分:1)
似乎您描述此模型的方式是CalculationsAbstract
中的两个方法始终被覆盖。为什么不将它设为接口ICalculationsAbstract
并具有
internal interface IScenarioACalculations : ICalculationsAbstract
{
float GetScenarioAValue5();
}
你仍然可以:
ScenarioACalculations : Calculations, IScenarioACalculations
ScenarioACalculationsCache : CalculationsCache, IScenarioACalculations
但现在您的IScenarioACalculations
可以投放到ICalculationsAbstract
答案 2 :(得分:1)
我仍然看到总是必须修改所有内容以添加更多计算的问题。也许您的ICalculations
应该有Calculate()
个IScenario
方法。 IScenario
将提供执行场景和返回的方法,检查相等性的方法,获取哈希码,以及可能的克隆方法。
我们的想法是,您要添加的每个新计算都是一个单独的类。它们每个都可以创建并传递给ICalculations
,然后可以决定如何处理它。很可能实现会针对某种缓存检查IScenario's
哈希码,如果找到则返回该值,如果没有调用IScenario's Execute()
存储带有IScenario's
哈希码的值进行缓存然后返回
所以它基本上是Command Pattern。我看到你获得的是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);
我添加了克隆方法,以防属性可写,您不想修改缓存方案的参数并取消验证存储的值。