将许多代码块编译成单个方法

时间:2015-11-16 23:15:12

标签: c# reflection compilation refactoring reflection.emit

我有一种遗留方法可以实时处理各种数量。有很多数据,这个方法基本上是一个很大的if / switch混乱,它决定了如何根据某些规则计算目标值,并为从每个设备收到的每个样本执行此操作(和他们中有很多人)。它的签名就像:

double Process(ITimestampedData data, IProcessingRule rule);

其中ISample包含单个时间戳的多个不同数量的值,而IProcessingRule定义要使用的值以及如何处理它以获得结果(然后可以将其与阈值进行比较)

我想摆脱所有ifswitch es并将其重构为一个工厂,它将为每个规则创建一个处理方法,然后为输入数据运行这些方法。由于这些规则有各种参数,我还想看看是否有办法在编译时完全解析所有这些分支(好吧,运行时,但我指的是我调用工厂方法一次的点“编译”我的处理代表。)

所以,我有类似的东西,但更复杂(更多相互依赖的条件和各种规则):

 // this runs on each call
 double result;
 switch (rule.Quantity)
 {
     case QuantityType.Voltage: 
     {
          Vector v;
          if (rule.Type == VectorType.SinglePhase)
          {
             v = data.Vectors[Quantity.Voltage].Phases[rule.Phase];
             if (rule.Phase == PhaseType.Neutral)
             {
                 v = v * 2; // making this up just to make a point
             }
          }
          else if (rule.Type == VectorType.Symmetry)
          {
              v = CalculateSymmetry(data.Vectors);
          }

          if (rule.TargetProperty == PropertyType.Magnitude)
          {
              result = v.Magnitude();
              if (rule.Normalize) 
              {
                   result /= rule.NominalValue;
              }
          }
     }

     // ... this doesn't end so soon

这样的事情:

// this is a factory method which will return a single delegate
// for each rule - and do it only once, at startup
Func<ITimestampedData, double> GetProcessor(IProcessingRule) 
{
    Func<ITimestampedData, Vectors> quantityGetter;
    Func<Vectors, Vector> vectorGetter;
    Func<Vector, double> valueGetter;

    quantityGetter = data => data.Vectors[rule.Quantity];

    if (rule.Type == VectorType.SinglePhase)
    {
        if (rule.Phase == PhaseType.Neutral)
            vectorGetter = vectors => 2 * vectors.Phases[rule.Phase];
        else
            vectorGetter = vectors => vectors.Phases[rule.Phase];
    }
    else if (rule.Type == VectorType.Symmetry)
    {
        vectorGetter = vectors => CalculateSymmetry(vectors);
    }

    if (rule.TargetProperty == PropertyType.Magnitude)
    {
        if (rule.Normalize) 
            valueGetter = v => v.Magnitude() / rule.NominalValue;
        else 
            valueGetter = v => v.Magnitude();
    }
     ...

    // now we just chain all delegates into a single "if-less" call
    return data => valueGetter(vectorGetter(quantityGetter(data)));
 }

但问题是:

  1. 我的方法中仍然有很多重复,
  2. 我已将if切换为多个委托调用,性能不会更好,
  3. 虽然这个“链”是固定的并且在工厂方法结束时已知,但我仍然没有一个可以处理输入的编译方法。
  4. 所以,最后,我的问题是:

    有没有办法以某种方式从我工厂内的各种代码块中“构建”最终的编译方法?

    我知道我可以使用CSharpCodeProvider之类的东西,创建一个巨大的字符串然后编译它,但我希望有更好的编译时支持和类型检查。

2 个答案:

答案 0 :(得分:1)

<强>工厂

switch语句通常是代码中的难闻气味,你对它的感觉是完全正确的。但工厂对于switch语句来说是完全有效的地方。只是不要忘记工厂的责任是构建对象,所以要确保任何额外的逻辑都在工厂之外。此外,不要将工厂与工厂方法混淆。当您拥有一组可多态交换的类并且您的工厂决定使用哪一个类时,首先使用它。此外,它有助于打破依赖关系。同时,工厂方法更像是静态构造函数,它们了解构造对象的所有依赖关系。我建议小心工厂方法,而不是选择合适的Factory类。从SRP的角度考虑这一点 - 工厂的责任是在您的班级负有一定的业务责任时构建对象。虽然您使用工厂方法,但您的课程有两个职责。

<强>压痕

我尝试遵循一个很好的规则,称为&#34;每种方法一个缩进级别&#34;。这意味着,您只能拥有更多级别的缩进,不包括根缩进。这是有效且可读的代码:

function something() {
    doSomething();

    if (isSomethingValid()) {
        doSomethingElse();
    }

    return someResult();
}

尝试遵循此规则,通过提取私有方法,您将看到代码变得更加清晰。

If / Else语句

事实证明,else语句始终是可选的,您可以随时重构代码以避免使用它。解决方案很简单 - 使用早期退货。您的方法将变得更短,更易读。

可能我的答案不足以解决你所有的问题,但至少它会给你一些思考的想法。

如果您正在使用遗留代码,我强烈建议您阅读&#34;有效使用遗留代码&#34; Michael Feathers的书,当然还有#34; Refactoring&#34;作者:Martin Fowler。

答案 1 :(得分:0)

考虑让规则在其中包含更多功能。 你知道你想要什么规则,因为你传递了它。但是在你当前的代码中,你要求自己运行以确定你做什么计算。我建议你让规则更加智能,并询问结果的规则。

例如,执行计算最多的规则是SinglePhaseNeutralVoltageMagnitudeNormalizedRule。

class SinglePhaseNeutralVoltageMagnitudeNormalizedRule implements IProcessingRule
{
    double calculate(ITimestampedData data)
    {
        double result;
        Vector v;
        v = data.Vectors[Quantity.Voltage].Phases[Phase];
        v = v * 2; // making this up just to make a point
        result = v.Magnitude();
        result /= NominalValue;
        return result;
    }
}

因此,Process方法变得更加简单

result = rule.calculate(data);

如果存在很多复杂性,可以使用@SergeKuharev建议的工厂类来构建规则。此外,如果规则本身之间有许多共同的代码可以重构到一个共同的地方。

例如,规范化可能是一个简单包装另一个规则的规则。

class NormalizeRule IProcessingRule
{
    private IProcessingRule priorRule;
    private double nominalValue;
    public NormalizeRule(IProcessingRule priorRule, double nominalValue)
    {
        priorRule = priorRule;
        nominalValue = nominalValue;
    }
    public double calculate(ITimestampedData data)
    {
        return priorRule.calculate(data)/nominalValue;
    }
}

所以,如果给出一个类SinglePhaseNeutralVoltageMagnitudeRule(如上所述减去/ = nominalValue),工厂可以将两者结合起来,通过组合制作SinglePhaseNeutralVoltageMagnitudeNrmalizedRule。