【问题标题】:Refactor help c#重构帮助 c#
【发布时间】:2009-08-13 18:15:09
【问题描述】:

我有几百行这样的代码:

if (c.SomeValue == null || c.SomeProperty.Status != 'Y')
{
    btnRecordCall.Enabled = false;
}

if (c.SomeValue == null || (c.SomeProperty.Status != 'Y' &&
    c.SomeOtherPropertyAction != 'Y'))
{
    btnAddAction.Enabled = false;
}

if (c.SomeValue == null || c.SomeProperty.Processing != 'Y')
{
    btnProcesss.Enabled = false;
}

如何正确重构它?我看到每次都调用检查'c.SomeValue == null',但它包含在其他条件中。我怎样才能消除这个重复的代码?

【问题讨论】:

    标签: c# design-patterns refactoring


    【解决方案1】:

    我会使用specification pattern,并构建映射到适当的启用值的复合规范。

    您要回答的总体问题是某个对象 c 是否满足给定条件,然后您可以决定是否要启用某些功能。那么你就有了这个界面:

    interface ICriteria<T>
    {
        bool IsSatisfiedBy(T c);
    }
    

    那么您的代码将如下所示:

    ICriteria<SomeClass> cr = GetCriteria();
    
    btnAddAction.Enabled = cr.IsSatisfiedBy(c);
    

    下一步是编写一个合适的 ICriteria 对象。您可以有另一个 ICriteria 实现(除了 Or 和 And),称为 PredicateCriteria,如下所示:

    class PredicateCriteria<T>  : ICriteria<T>
    {
        public PredicateCriteria(Func<T, bool> p) {
            this.predicate = p;
        }
    
        readonly Func<T, bool> predicate;
    
        public bool IsSatisfiedBy(T item) {
            return this.predicate(item);
        }
    }
    

    一个例子是:

    var c = new PredicateCriteria<SomeClass>(c => c.SomeValue != null);
    

    剩下的就是这个与其他标准的组合。

    【讨论】:

      【解决方案2】:

      如果你不想做太多重构,你可以很容易地把空检查拉出来。

      if (c.SomeValue == null)
      {
          btnRecordCall.Enabled = false;
          btnAddAction.Enabled = false;
          btnProcesss.Enabled = false;
      }
      else
      {
          if(c.SomeProperty.Status != 'Y')
          {
              btnRecordCall.Enabled = false;
          }
      
          if((c.SomeProperty.Status != 'Y') &&
             (c.SomeOtherPropertyAction != 'Y'))
          {
              btnAddAction.Enabled = false;
          }
      
          if(c.SomeProperty.Processing != 'Y')
          {
              btnProcesss.Enabled = false;
          }
      }
      

      如果您希望重构而不是随机播放,则可以将布尔测试墙移至对象 c 是其实例的任何类的方法/扩展方法中 - 您可以这样说

      btnRecordCall.Enabled = c.IsRecordCallAllowed();
      

      【讨论】:

      • 我采用了第二种方法。扩展了部分类并实现了这些行为。谢谢! ~ck
      【解决方案3】:

      具体而言,由于您似乎在设置 UI 元素状态,因此您可以考虑更多 two-way data binding model,您可以在其中设置数据上下文和控件到属性的映射,并让其管理控件状态。您还可以考虑一个更重量级的解决方案,例如来自Enterprise LibraryValidation Application Block。还有一些fluent validation projects你应该看看。

      【讨论】:

        【解决方案4】:

        在“c”上创建属性,例如“CanRecordCall”、“CanAddAction”、“CanProcess”,以便您的代码变成这样:

        btnRecordCall.Enabled = c.CanRecordCall;
        btnAddAction.Enabled = c.CanAddAction;
        btnProcess.Enabled = c.CanProcess;
        

        “c.SomeValue == null”是对 NullReferenceExceptions 的典型响应。您可以通过将其 SomeValue 属性初始化为 null object 来改进“c”,这样就永远不会有空引用(只是一个什么都不做的对象)。

        【讨论】:

          【解决方案5】:

          我首先要确保所有此类代码都是连续的。除此代码之外的任何内容都应移到代码之前或之后。

          然后,对于每个控件属性的引用,创建一个相应的局部变量,例如,processEnabled。在第一个 if 语句之前定义它。对于每个这样的属性,将 btnProcesss.Enabled = false; 移动到此代码块的末尾,并将“false”更改为 processEnabled。将原来的替换为processEnabled = false;

          当代码块不再引用控件(或与 UI 相关的任何其他内容)时,选择整个块,从添加的变量到最后的控件属性集,并使用提取方法重构.这应该会给您留下一个接受c 的方法,并生成您以后可以用来设置控件属性的值。

          你甚至可以变得更漂亮。定义一个具有这些“变量”作为属性的类,而不是单个局部变量。做几乎相同的事情,提取的方法最终将返回该类的实例,而不是单独的 out 参数。

          从那里开始,您可能会开始在提取的方法中看到更多需要清理的内容,而不是从该代码中删除与 UI 相关的任何内容。

          【讨论】:

            【解决方案6】:

            我猜这里的问题是关于“布尔映射”风格的重构,即能够重构可能存在一些差距和一些重复的互补布尔情况。好吧,如果这就是你所追求的,你当然可以编写一个工具来做到这一点(这就是我会做的)。基本上,您需要解析一堆if 语句并记下所涉及的条件组合。然后,通过一些相当简单的逻辑,你可以让你的模型吐出一个不同的、更优化的模型。

            上面显示的代码是我喜欢 F# 的原因之一。 :)

            【讨论】:

              【解决方案7】:

              有趣的是,在我们当前的 Winforms 应用程序中,这三个条件将位于三个不同的类中,因为每个按钮都将附加到不同的 Command

              条件将在命令的 CanExecute 方法中,并控制触发命令的按钮的启用/禁用行为。对应的执行代码在类的Execute方法中。

              【讨论】:

                猜你喜欢
                • 2011-03-29
                • 1970-01-01
                • 1970-01-01
                • 2010-11-19
                • 1970-01-01
                • 1970-01-01
                • 2011-10-05
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多