2010-10-26 68 views
1

我有以下类别:更好的重构方法?

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()。

第二个选项是在派生类级别使用可空类型,因为我不一定需要计算在BaseClass的构造函数来完成,懒惰计算可能是一个更好的选择:

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

    // Do computation 
    cachedValue = ... 

    return cachedValue; 
}   

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

你对此有何看法?

谢谢。

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

回答

1

另一种方法是对Baseclass添加一个公共接口方法GetValue,只允许覆盖ComputeValue继承类的方法 - 这种方式仍然可以扩展功能,但您可以控制基类中ComputeValue结果的行为/上下文,即可以像在示例中那样添加记忆或根据需要装饰/扩展。

这跟在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; 
      } 

     } 
    } 
+0

但随后其他程序员不是很熟悉的代码可能只是使用ComputeValue()导致性能问题 – alhazen 2010-10-26 20:26:42

+0

更好的负担是对程序员真正了解他们是不是在你的类的用户在做什么 - 这种方法确实有一个干净的“外部”接口,但它当然仍然需要程序员修改继承的类来理解基类的接口。 – BrokenGlass 2010-10-26 20:32:27

+0

你说得对,最好让事情简单些。谢谢。 – alhazen 2010-10-27 14:37:07

3

为什么不在构造函数中使用与ComputeValue具有相同目的的delagate,然后将该值作为protected readonly字段公开?

public abstract class BaseClass { 
    protected readonly double cachedValue; 

    protected BaseClass(Func<double> computeValue) { 
    cachedValue = computeValue(); 
    } 
} 
1

在需要计算值的逻辑和计算值的逻辑之间是否存在相关链接?

如果不是,或者至少没有100%匹配,则可以将计算逻辑放在单独的类中:CalculatorA和CalculatorB,它们都从接口ICalculator继承。然后Base类可以是唯一访问此接口并缓存结果的类。

2

在某些时候,你将不得不初始化这个值,这是没有办法的。所以我认为使用可空值的方法是有道理的 - 我同意它比在那里有一个缓存属性更清晰。

你可能虽然想添加一个参数ComputeValue迫使值再次计算:

protected override double ComputeValue(bool force) 
{ 
    if(!force && cachedValue.HasValue) 
    { 
     return cachedValue; 
    } 

    // Do computation 
    cachedValue = ... 

    return cachedValue; 
}   
1

我觉得ComputeValue应该懒洋洋地称为一个属性的getter:

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

} 
+1

这样做的缺点是从这个类派生的用户可能会错误地调用'ComputeValue()'。在这种情况下,我会将'ComputeValue'设为私有,并指示该类的用户重写'cachedValue'。当然,这就消除了派生类在他们自己的ComputeValue版本中调用'base.ComputeValue()'的能力,但是如果set被设置为protected,他们仍然可以使用'base.cachedValue'来做到这一点......引入了类似的问题,但我不认为用户会错误地设置属性。 – Brian 2010-10-26 20:36:35

+0

@Brian好点布赖恩。想一想,并从Jare​​dPar中排队等待混合方法。 ChildClass在构造时传递委托。 BaseClass.cachedValue getter引用该委托的私有引用。 – Sorax 2010-10-26 20:46:56

2

如果你在NET 4just使用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

一个总体良好一块建议是总是使用构图而不是继承:)

+0

谢谢,这真的很有用。不幸的是,我仍然坚持.NET 3.5 – alhazen 2010-10-26 20:44:11

+1

更新我的答案,因为你在.NET 3.5 – 2010-10-26 20:51:20

1

为了存储/缓存计算值,你可以使用Singleton Pattern,基本上你声明了一个静态字段,然后在尝试之前检查null值 计算。所以只有在方法或属性第一次被调用/加入时才会计算出计算值。如果某个派生类需要不同的计算值,那么您将覆盖基类方法/属性(虚拟修饰符用于确保多态性)。对于表示类的一种数据的元素,建议使用属性而不是方法。

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