2011-05-16 71 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

我发现测试驱动开发也经常促使我朝着更好的设计方向发展。在你的情况下,'翻译方法'是一个重要的部分,你会想独立于他们在课堂上的使用进行测试。我会建议将该逻辑封装在自己的类型中。

然后,您可以关注实例,因为里德建议您知道您的翻译逻辑经过充分测试。然后,您的抽象基类只是作为一个知道如何获得正确译者的共同根。