当一个类型需要额外的属性时,尝试实现多态性

时间:2011-11-10 17:02:05

标签: c# asp.net .net polymorphism

我有两个界面:

interface IDynamicControl
{
    string Id { get; set; }
    string Label { get; set; }
    string Value { get; set; }
}

interface IDynamicList : IDynamicControl
{
    IList ListItems { get; set; }
}

我有一个返回ControlResolver的{​​{1}}类 - 为了我的目的,我有ASP.NET TextBoxes,CheckBoxes和DropDownLists的包装类,它们实现了IDynamicControl(DropDown实现了IDynamicList)。要点是我可以给解析器一个控件的名称,例如“Textbox”,它会给我一个IDynamicControl,它是一个修改过的ASP.NET文本框。

这很有效,但问题是DropDownList。也许我有一个大脑放屁,但我遇到的问题是,当控件是下拉列表时,我必须对IDynamicList进行显式转换(因为解析器返回IDynamicControl)所以我可以添加项目到它用于显示。这通常不是问题,但动态控制的目的是我可以在外部存储字段类型并将其读入,因此我必须做一些丑陋的事情:

IDynamicControl

但这看起来相当丑陋。我可以在基类中包含ListItems属性,只是在不使用它的类中抛出NotImplemented,但这违反了ISP,甚至比if语句更有气味。总之,我想让我的解析器返回一种控件,所以我不需要使用不同的Resolve()方法,但是在使用代码时,如果只有控件那么我需要做额外的工作是一个下拉列表。

除非我弄错了或者我忘记了一些基本的东西,否则有更好的解决办法,或者我应该使用if语句?我不能使用基类,因为我的所有“动态”类都继承自基础ASP.NET UI类。

4 个答案:

答案 0 :(得分:2)

单个方法不能返回不同的类型,除非它是非泛型的。在您的情况下,您无法传递泛型类型参数,因此您必须应用某种显式强制转换。此外,由于您有不同的设置逻辑,您还必须应用一些条件检查。

你可以摆脱的一件事是条件if检查(想象一下在10种控件的情况下需要多少if's yu),你可以使用IDictionary作为缓存初始化函数,以便代码更干净:

IDictionary<string, Func<IDynamicControl>> setupWorkerProvider

然后在单行中解析相应的设置功能:

// once setup setup provider
setupWorkerProvider.Add("dropdown", (dynamicControl) => { /*setup logic here*/ });
setupWorkerProvider.Add("button", (dynamicControl) => { /*setup logic here*/ });

// resolve initializer
var setupWorker = setupWorkerProvider[controlType];

// initialize control
setupWorker(control);

答案 1 :(得分:0)

这是应该的。 ResolveControl方法提供了一个合同保证,它将返回一个IDynamicControl。它并不保证它会返回任何比这更具体的内容。

想象一下,如果您有以下代码:

public Object foo(int x) {
    if (x%2 == 0) {
        return new String("Hello");
    } else {
        return new Integer(1);
    }
}

您是否希望调用者拥有所有String方法和所有可用的Integer方法?

答案 2 :(得分:0)

我认为将原始代码重构为更简洁的最简单方法如下:

        var control = ControlResolver.ResolveControl(controlType);

        // ... do common logic for all controls

        var list = control as IDynamicList;
        if (list != null)
        {
            // .. do additional logic for list controls
        }

调用ControlResolver.ResolveControl的代码不应该真正了解“下拉”控件的任何特殊规则。它真正需要知道的是解析的控件是否实现了IDynamicList接口。

当然,你仍然需要一些条件逻辑,但至少条件是基于你的代码已经知道的众所周知的接口,而不是底层的实现细节。

当然,您可以采取其他方法来进一步沿着抽象的路径前进,但这似乎是一个小的变化,可以在不影响任何其他代码的情况下进行。

答案 3 :(得分:0)

我建议,既然你知道你想要一个IDynamicList你有一个返回一个的解析器方法(ListControlResolver?)。也许它只是调用你现有的解析器并在返回之前进行转换,但你作为调用者并不关心它。哎呀,当你想要管理其他列表控件时,这可能会在以后发挥作用。