更好的重构方法呢?

时间:2010-10-26 20:03:24

标签: c# .net inheritance refactoring

我有以下课程:

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()是耗时的计算。

问题是,ClassAClassB中还有其他方法需要从ComputeValue()返回的值。我想在BaseClass中添加一个名为'CachedValue'的受保护属性,但我发现这种方法是多余的,并且可能会让其他可能不知道它存在的程序员感到困惑,并且可能直接调用ComputeValue()。

第二个选项是在派生类级别使用nullable类型,因为我不一定要求在BaseClass的构造函数中完成计算,延迟计算可能是更好的选择:

protected override double ComputeValue()  
{
    if(cachedValue.HasValue)
    {
        return (double) cachedValue;
    }

    // Do computation
    cachedValue = ...

    return cachedValue;
}        

但我觉得我可以做得更好。

您对此有何看法?

谢谢。

编辑:只是为了澄清,我试图通过强制使用“ComputeValue()”来阻止cachedValue被多次调用。

7 个答案:

答案 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.
}