令人厌恶的黑客(基类上使用反射的工厂方法)

时间:2009-11-10 04:54:13

标签: c# .net reflection factory factory-pattern

这是一件很脏的事情,我觉得这样做很脏:

public abstract class InterestRate {

    // irrelevant details

    public static T ImpliedRate<T>(
        double factor,
        double time,
        DayCounter dayCounter
    ) where T : NonCompoundedInterestRate {
        MethodInfo methodInfo = typeof(T).GetMethod(
            "ImpliedRate",
            BindingFlags.Static);
        return (T)methodInfo.Invoke(
            null,
            new object[] { factor, time, dayCounter }
        );
    }

    public static T ImpliedRate<T>(
        double factor,
        double time,
        DayCounter dayCounter,
        Frequency frequency
    ) where T : CompoundedInterestRate {
        MethodInfo methodInfo = typeof(T).GetMethod(
            "ImpliedRate",
            BindingFlags.Static);
        return (T)methodInfo.Invoke(
            null,
            new object[] { factor, time, dayCounter, frequency }
        );
}

这里我有来自抽象类NonCompoundedInterestRate的类CompoundedInterestRate(摘要)和InterestRate。我有几个NonCompoundedInterestRate的具体实现,它们具有名为ImpliedRate的静态方法,并具有相应的签名,以便上述反射可以工作。

使用反射来调用一个静态方法,该方法甚至不能保证在派生类中存在,只需要查看。有没有更好的方法来解决这个问题?

6 个答案:

答案 0 :(得分:2)

你应该感觉很脏。这是一些肥皂:

public static class InterestRateFactories
{
    static InterestRateFactories()
    {
        _factories = new List<IInterestRateFactory>();
        //  register default factories, although you can also register them elsewhere, like in your ioc setup
    }
    private static readonly List<IInterestRateFactory> _factories;
    public static void RegisterFactory(IInterestRateFactory factory)
    {
        _factories.Add(factory);
    }
    public static T ImpliedRate<T>(double factor, double time, DayCounter dayCounter)
        where T : NonCompoundedInterestRate
    {
        var factory = _factories.FirstOrDefault(x => x.CanCreate(typeof(T), false));
        if (factory == null)
        {
            throw new NotSupportedException("Cannot create a non-compounded implied interest rate of type " + typeof(T).Name);
        }
        return (T)factory.Create(factor, time, dayCounter);
    }
    public static T ImpliedRate<T>(double factor, double time, DayCounter dayCounter, Frequency frequency)
        where T : CompoundedInterestRate
    {
        var factory = _factories.FirstOrDefault(x => x.CanCreate(typeof(T), false));
        if (factory == null)
        {
            throw new NotSupportedException("Cannot create a compounded implied interest rate of type " + typeof(T).Name);
        }
        return (T)factory.Create(factor, time, dayCounter, frequency);
    }
}

public interface IInterestRateFactory
{
    bool CanCreate(Type nonCompoundedInterestRateType, bool compounded);
    NonCompoundedInterestRate Create(double factor, double time, DayCounter dayCounter);
    CompoundInterestRate Create(double factor, double time, DayCounter dayCounter, Frequency frequency);
}

答案 1 :(得分:1)

似乎调用者可以轻松地在派生类上调用工厂方法,因为调用此方法将派生类型作为T传递。

这里稍微更明确的合同是向T添加new()约束,调用默认ctor,然后调用在基类上定义的Init抽象方法。

工厂模式具有可测试性优势,但不是您在此处使用它。第三种方法是让调用者传递一个工厂类的实例来使用(ImpliedRate方法将在工厂接口上)。这对于单元测试很方便,但对于API的消费者来说可能是繁重的。

答案 2 :(得分:1)

您可以使用常规方法以及修改过的Clone / Prototype模式等替代静态方法。例如:

public static class InstanceMap
{
    private static readonly Dictionary<Type,object> instances = 
        new Dictionary<Type,object>();

    public static void AddInstance(object instance)
    {
        instances[instance.GetType()] = instance;
    }

    public static T GetInstance<T>() { return (T) instances[typeof(T)]; }  
}

public interface INonCompoundedInterestRate
{
    INonCompoundedInterestRate ImpliedRate(double factor,
        double time,
        DayCounter dayCounter);
}

public class MyNonCompoundedInterestRate: INonCompoundedInterestRate
{
    public INonCompoundedInterestRate ImpliedRate(double factor,
        double time,
        DayCounter dayCounter) { /* do smth*/ }

    static MyNonCompoundedInterestRate()
    {
        InstanceMap.AddInstance(new MyNonCompoundedInterestRate());
    } 
} 

public abstract class InterestRate {
    public static T ImpliedRate<T>(
        double factor,
        double time,
        DayCounter dayCounter
    ) where T : INonCompoundedInterestRate 
    {
        return InstanceMap.GetInstance<T>().
            ImpliedRate(factor, time, dayCounter);
    }
    // ...
}

答案 3 :(得分:1)

根据我的经验,你只能实例化一个参数而不是泛型的构造函数。

你想要实现的目标只能通过反思来实现。

答案 4 :(得分:1)

尝试将静态(工厂)方法与继承混合时,您总是会遇到冲突。很难获得您正在寻找的多态行为。我有类似的问题,目前正在使用反射。如前所述,另一个选择是不使用静态方法(如果不需要)。然后,您可以使用模板方法或任何其他适用于继承的策略。

答案 5 :(得分:0)

任何理由不仅仅在通用基础上声明的非泛型接口上定义那些,然后对T进行转换?我没有看到任何通用的args被传递......