2010-10-26 70 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. 
}