使用许多if和duplicated逻辑从不同的Controls类中提取值来重构代码的最佳方法是什么

时间:2011-10-21 13:12:25

标签: c# design-patterns refactoring

我在WPF应用程序中有以下代码

if (panel != null)
{
     IList listOfValues = new ComparableListOfObjects();
        var childControls = panel.GetChildren<Control>(x => x.Visibility == Visibility.Visible);
 foreach (Control childControl in childControls)
 {
    var textBox = childControl as TextBox;
    if (textBox != null)
    {
        listOfValues.Add(textBox.Text);
        continue;
    }

    var comboBox = childControl as ComboBox;
    if (comboBox != null)
    {
        listOfValues.Add(comboBox.SelectedItem);
        continue;
    }

    var datePicker = childControl as DatePicker;
    if (datePicker != null)
    {
        listOfValues.Add(datePicker.SelectedDate.GetValueOrDefault());
        continue;
    }
    var numericBox = childControl as NumericUpDown;
    if (numericBox != null)
    {
        listOfValues.Add(numericBox.Value);
        continue;
    }

}

重构这个代码的最佳方法是什么,重复相同的逻辑,从不同的控件中提取值,如?

        var numericBox = childControl as NumericUpDown;
    if (numericBox != null)
    {
        listOfValues.Add(numericBox.Value);
        continue;
    }

在其他方法的同一个类中有相同的代码

        private static object GetControlValue(Control control)
    {
        if (control == null)
            throw new ArgumentNullException("control");

        var textBox = control as TextBox;
        if (textBox != null)
            return textBox.Text;

        var comboBox = control as ComboBox;
        if (comboBox != null)
            return comboBox.SelectedValue;

        var datePicker = control as DatePicker;
        if (datePicker != null)
            return datePicker.SelectedDate.GetValueOrDefault();

        var numericUpDown = control as NumericUpDown;
        if (numericUpDown != null)
            return numericUpDown.Value;

        throw new NotSupportedException();
    }

我可以使用策略设计模式,但在这种情况下,我需要为每种类型的控件创建其他类吗?

你能否建议我更好地适应这个问题? 提前谢谢。

5 个答案:

答案 0 :(得分:6)

拥有ifswitch语句并不是件坏事。即使进行一些基本类型检查也不一定是坏事,特别是当使用的类型不能以多态方式使用时。让这个逻辑不止一次地表达是不赞成的,因为你正在重复自己,并且你有相同变化的多个维护点。

在原始代码段中,您可以

var blah = obj as Foo;
if (blah != null)
{
    someList.Add(blah.Value); 
}

对于其他几种控件类型重复此操作。但是后来在你的私有方法中,你基本上有相同的逻辑表示相同的次数。

var blah = obj as Foo;
if (blah != null)
    return blah.Value;

唯一的区别是,在第一个代码段中,您获取值并将其添加到列表中。在第二个中,您返回值。第一个代码片段取消及其类型检查逻辑,它应该使用已经在另一个方法中表达的逻辑。

foreach (var control in childControls)
{
    listOfValues.Add(GetControlValue(control));
}

这个想法是不要重复自己。 DRY。

答案 1 :(得分:1)

我相信你正在寻找Visitor pattern。每个控制器一个类是一种方法,但引用引用的文章:

  

注意:更灵活的方法是创建一个包装器   实现定义accept方法的接口的类。该   包装器包含一个指向CarElement的引用,它可以是   通过构造函数初始化。这种方法避免了必须   在每个元素上实现一个接口。 [见文章Java技巧98   下面的文章]

你或许可以逃脱这个。

答案 2 :(得分:1)

有点黑客攻击,但是这里有一种方法可以使用委托和集合初始化程序来消除冗余(你可能不愿意按原样使用它,而是想法)。

首先创建一个这样的类:

// Needs argument validation. Also, extending Dictionary<TKey, TValue>
// probably isn't a great idea.
public class ByTypeEvaluator : Dictionary<Type, Func<object, object>>
{
    public void Add<T>(Func<T, object> selector)
    {
        Add(typeof(T), x => selector((T)x));
    }

    public object Evaluate(object key)
    {
        return this[key.GetType()](key);
    }
}

然后用法变为:

// Give this variable longer lifetime if you prefer.
var map = new ByTypeEvaluator
{
    (ComboBox c) => c.SelectedItem,
    (TextBox t) => t.Text,
    (DateTimePicker dtp) => dtp.Value,
    (NumericUpDown nud) => nud.Value
};

Control myControl = ...
var myProjection = map.Evaluate(myControl); 

答案 3 :(得分:1)

您可以在通用方法中将其作为案例选择执行,但仍有一些使用此样式的工作:

public static string GetValue<T>(T obj) where T:Control
        {
            switch (obj.GetType().ToString())
            {
                case "TextBox":
                    return (obj as TextBox).Text;
                    break;
                case "ComboBox":
                    return (obj as ComboBox).SelectedValue.ToString();
                    break;

                    //..etc...
            }   
        }

答案 4 :(得分:-1)

你可以使用这样的方法,依靠is

private static object GetControlValue(Control control)
{
    if (control == null)
        throw new ArgumentNullException("control");

    if (control is TextBox) return (control as TextBox).Text;
    if (control is ComboBox) return (control as ComboBox).SelectedValue;

...
}

if (panel != null)
{
    IList listOfValues = new ComparableListOfObjects();
    var childControls = panel.GetChildren<Control>(x => x.Visibility == Visibility.Visible);
    foreach (Control childControl in childControls)
    {
    if(childControl is TextBox) { listOfValues.Add((childControl as TextBox).Text);     continue; }
    if(childControl is ComboBox) { listOfValues.Add((childControl as ComboBox).SelectedValue); continue; }

    ... 

    }
}

第二个区块中的continue可能甚至不需要,但需要进行一些测试。