重构帮助c#

时间:2009-08-13 18:15:09

标签: c# design-patterns refactoring

我有几百行代码:

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',但它包含在其他条件中。我怎么可能消除这个重复的代码?

7 个答案:

答案 0 :(得分:5)

我会使用specification pattern,并构建映射到正确的Enabled值的复合规范。

您要回答的整体问题是某个对象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);

其余的将是其他标准的组成。

答案 1 :(得分:4)

如果您不想进行太多重构,可以轻松地进行空值检查。

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;
    }
}

如果你想重构而不是shuffle,布尔测试的墙可以移动到你的对象c是实例的任何类的方法/扩展方法中 - 这样你可以说

btnRecordCall.Enabled = c.IsRecordCallAllowed();

答案 2 :(得分:2)

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

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

“c.SomeValue == null”是对NullReferenceExceptions的典型响应。您可以通过将其SomeValue属性初始化为null object来改进“c”,以便永远不会有空引用(只是一个不执行任何操作的对象)。

答案 3 :(得分:2)

具体来说,由于您似乎正在设置UI元素状态,因此您可以考虑设置数据上下文和控制到属性映射的更多two-way data binding model,并让它控制控件状态。您还可以考虑更加重量级的解决方案,例如来自Validation Application BlockEnterprise Library。还有一些fluent validation projects你应该看看。

答案 4 :(得分:1)

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

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

当代码块不再引用控件(或与UI有关的任何其他内容)时,选择整个块,从添加的变量到最后的控件属性集,并使用Extract Method重构。这应该为您提供一个接受c的方法,并生成稍后可用于设置控件属性的值。

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

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

答案 5 :(得分:0)

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

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

答案 6 :(得分:0)

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

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