2011-05-16 31 views
6

我遇到類似的問題Accessing a static property of a child in a parent method。首選答案提示類的設計有缺陷,需要更多信息來討論問題。在父方法中訪問子項的靜態屬性 - 設計注意事項

這是我想和你討論的情況。

我要實現一些單位知道的數據類型,例如,長度,質量,目前,... 應該有一個隱式轉換爲給定字符串創建實例。例如「1.5米」應該與「150釐米」相同,或者「20英寸」應該正確處理。

爲了能夠在不同的單位之間進行轉換,我需要數量特定的轉換常數。 我的想法是用一些靜態的轉換方法創建一個抽象基類。 那些應該使用類特定的靜態定義字典來完成他們的工作。 所以看看這個例子。

public class PhysicalQuantities 
{ 
    protected static Dictionary<string, double> myConvertableUnits; 

    public static double getConversionFactorToSI(String baseUnit_in) 
    { 
     return myConvertableUnits[baseUnit_in]; 
    } 
} 

public class Length : PhysicalQuantities 
{ 
    protected static Dictionary<string, double> myConvertableUnits = new Dictionary<string, double>() 
    { 
     { "in", 0.0254 }, { "ft", 0.3048 } 
    }; 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     Length.getConversionFactorToSI("in"); 
    } 
} 

我認爲這給出了一個相當直觀的用法,並保持代碼緊湊,相當易讀和可擴展。但我當然遇到了引用的post所描述的相同問題。

現在我的問題是:我如何通過設計避免這個問題?

+0

我想知道是否有定義爲'f(double)'的轉換會讓你陷入麻煩。這可能是一個轉換需要別的東西。 'Func '或'Func '可能會更好。 – Hogan 2011-05-16 17:51:17

+0

你可以只是從靜態去靜態 - 我不認爲你會失去很多 – 2011-05-16 17:55:06

回答

4

我認爲這可以用泛型來解決,仍然看起來可讀。根據Slaks提出的建議,將註冊符合靜態構造函數,使其本身線程安全。

所以,如果我沒有記錯的話:

  • 線程安全(在靜態構造函數字典中的所有工作)
  • 語法仍然易於使用和可讀性SIConversion<Length>.GetFactor()(1個字符以上)
  • 代碼需要在衍生類實現非常型register(string,double);(實際上比您的字典定義更短)

    interface ISIConversionSubscriber 
    { 
        void Register(Action<string, double> regitration); 
    } 
    
    static class SIConversion<T> where T : ISIConversionSubscriber, new() 
    { 
    
        private static Dictionary<string, double> myConvertableUnits = new Dictionary<string, double>(); 
    
        static SIConversion() { 
         T subscriber = new T(); 
         subscriber.Register(registrationAction); 
        } 
    
        public static double GetFactor(string baseUnit) 
        { 
         return myConvertableUnits[baseUnit]; 
        } 
    
        private static void registrationAction(string baseUnit, double value) 
        { 
         myConvertableUnits.Add(baseUnit, value); 
        } 
    
    } 
    
    abstract class PhysicalQuantities : ISIConversionSubscriber 
    { 
        public abstract void Register(Action<string, double> register); 
    } 
    
    class Length : PhysicalQuantities 
    { 
        public override void Register(Action<string, double> register) 
        { 
         // for each derived type register the type specific values in this override 
         register("in", 1); 
        } 
    } 
    
    class Program 
    { 
        static void Main(string[] args) 
        { 
         Console.WriteLine(SIConversion<Length>.GetFactor("in")); 
        } 
    } 
    

輸出:1

如果你想知道爲什麼我做PhysicalQuantities摘要:避免使用它SIConversion<PhysicalQuantities>.GetFactor()因爲我們沒有爲基類轉換。無論如何,您可能不需要像這樣的基類的實例 - 它不是數量的完整表示,所以它可能只包含可重用的方法。

另一個建議是將一個枚舉用於baseUnit而不是一個字符串。由於每個人都在努力爭取類型安全,並且對魔術線上的犯規行爲有所恐慌,所以這可能是一條很好的道路:))

+0

http://stackoverflow.com/questions/686630/static-generic-class-as-dictionary – SLaks 2011-05-16 18:08:41

+0

我完全知道,螺紋安全需要鎖定。即使使用SIConversion < Length> .GetFactor(),也需要它,因爲字典本身不是線程安全的。所以這一切都是編碼語法首選項。如果兩個線程正在訪問單個SiConversion < Length>,則會發生同樣的情況。在任何情況下都需要鎖:/ – 2011-05-16 18:22:16

+1

靜態類可能也會更快,並且(IMHO)更優雅。 – SLaks 2011-05-16 18:23:22

3

這裏的最佳選擇通常傾向於避免設計的靜態性質,並改用實例。我有我已經開發了一個類似的庫,但使用更趨於這樣的:

static void Main() 
{ 
    // Since I'm working with instances, I tend to pass the actual 
    // measured amount in as well... However, you could leave this out (or pass 1) 
    var len = Length.Create(42, "in"); 
    double conversionFactory = len.ConversionFactorToSI; 
} 

我開發的這種方式讓我可以定義字典每種類型的,靜態的,但使用經過工廠方法這對基類的構造函數(這是保護)。這使得基類可以通過引用特定類型的字典來實例化,該字典工作得非常乾淨。

2

我發現測試驅動開發也經常促使我朝着更好的設計方向發展。在你的情況下,'翻譯方法'是一個重要的部分,你會想獨立於他們在課堂上的使用進行測試。我會建議將該邏輯封裝在自己的類型中。

然後,您可以關注實例,因爲裏德建議您知道您的翻譯邏輯經過充分測試。然後,您的抽象基類只是作爲一個知道如何獲得正確譯者的共同根。