我有以下课程:
public abstract class BaseClass
{
private readonly double cachedValue;
public BaseClass()
{
cachedValue = ComputeValue();
}
protected abstract double ComputeValue()
}
public class ClassA : BaseClass
{
protected override double ComputeValue() { ... }
}
public class ClassB : BaseClass
{
protected override double ComputeValue() { ... }
}
其中ComputeValue()
是耗时的计算。
问题是,ClassA
和ClassB
中还有其他方法需要从ComputeValue()
返回的值。我想在BaseClass中添加一个名为'CachedValue'的受保护属性,但我发现这种方法是多余的,并且可能会让其他可能不知道它存在的程序员感到困惑,并且可能直接调用ComputeValue()。
第二个选项是在派生类级别使用nullable类型,因为我不一定要求在BaseClass的构造函数中完成计算,延迟计算可能是更好的选择:
protected override double ComputeValue()
{
if(cachedValue.HasValue)
{
return (double) cachedValue;
}
// Do computation
cachedValue = ...
return cachedValue;
}
但我觉得我可以做得更好。
您对此有何看法?
谢谢。
编辑:只是为了澄清,我试图通过强制使用“ComputeValue()
”来阻止cachedValue
被多次调用。
答案 0 :(得分:3)
为什么不在构造函数中使用具有相同ComputeValue
目的的委托,然后将该值公开为protected readonly
字段?
public abstract class BaseClass {
protected readonly double cachedValue;
protected BaseClass(Func<double> computeValue) {
cachedValue = computeValue();
}
}
答案 1 :(得分:2)
在某些时候你必须初始化这个值,没有办法解决这个问题。因此,我认为使用可空值的方法是有道理的 - 我同意它比在那里拥有缓存属性更清晰。
您可能希望向ComputeValue添加一个参数,强制再次计算该值:
protected override double ComputeValue(bool force)
{
if(!force && cachedValue.HasValue)
{
return cachedValue;
}
// Do computation
cachedValue = ...
return cachedValue;
}
答案 2 :(得分:2)
如果您使用的是NET 4,请使用Lazy<T>
。
public class ClassA
{
Lazy<double> value = new Lazy<double>(()=>something.SomethingComplicated());
public void AnyMethod()
{
double d = value.Value;
//...
}
}
请参阅http://msdn.microsoft.com/en-us/library/dd642331.aspx
更新:由于你使用的是.NET 3.5,这里有一篇关于自己实现懒惰的文章(它只有大约20个LOC):http://msdn.microsoft.com/en-us/vcsharp/bb870976.aspx
一般来说,最好的建议是使用组合而不是继承:)
答案 3 :(得分:1)
需要计算值的逻辑与计算值的逻辑之间是否存在相关联系?
如果不是,或者至少没有100%匹配,则可以将计算逻辑放在单独的类中:CalculatorA和CalculatorB,它们都继承自ICalculator接口。然后,Base类可以是访问此接口并缓存结果的唯一类。
答案 4 :(得分:1)
另一种方法是在GetValue
上添加一个公共接口方法Baseclass
,并且只允许覆盖继承类的ComputeValue
方法 - 这样你仍然可以扩展功能,但是您可以在基类中控制ComputeValue
结果的行为/上下文,即您可以像示例中那样添加memoization,也可以根据需要添加decorate / extend。
这是Non-Virtual interface (NVI) pattern。
之后的内容 public abstract class BaseClass
{
private double? cachedValue;
public BaseClass()
{
}
protected abstract double ComputeValue();
public virtual double GetValue()
{
if (cachedValue.HasValue)
{
return (double)cachedValue;
}
else
{
cachedValue = ComputeValue();
return (double)cachedValue;
}
}
}
答案 5 :(得分:1)
我认为应该在属性getter中懒惰地调用ComputeValue:
public abstract class BaseClass
{
private Func<double> _computeValue;
private double? _cachedValue;
protected double cachedValue
{
get
{
if(_cachedValue == null)
_cachedValue = _computeValue();
return (double)_cachedValue;
}
private set
{
_cachedValue = value;
}
}
private BaseClass(){};
public BaseClass(Func<double> computeValue)
{
_computeValue = computeValue;
}
}
答案 6 :(得分:1)
对于存储/缓存计算值,您可以使用Singleton模式,基本上您声明一个静态字段,然后在尝试计算之前检查空值。因此,计算值仅在第一次调用/加入方法或属性时计算。如果某些派生类需要不同的计算值,则覆盖基类方法/属性(需要虚拟修饰符来确保多态性)。建议对表示类的数据类型的元素使用属性而不是方法。
public abstract class BaseClass {
private static double _cachedValue = null;
public BaseClass() { }
// base class implements a Singleton Pattern to store a computed value
protected virtual double ComputeValue
{
get
{
if( _cachedValue == null) { _cachedValue = CalculateComputedValue; }
return _cachedValue;
}
}
private double CalculateComputedValue() { return someComplexComputedValue; }
}
public class ClassA : BaseClass
{
private static double _cachedValue = null;
//this class does require calculate a specific computed value.
protected override double ComputeValue
{
get
{
if( _cachedValue == null) { _cachedValue = CalculateComputedValue; }
return _cachedValue;
}
}
private double CalculateComputedValue() { return someComplexComputedValue; }
}
public class ClassB : BaseClass
{
//this class does not require to calculate compute value, inherited value is used instead.
}