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

本文关键字:属性 多态性 实现 一个 类型 | 更新日期: 2023-09-27 18:14:23

我有两个接口:

interface IDynamicControl
{
    string Id { get; set; }
    string Label { get; set; }
    string Value { get; set; }
}
interface IDynamicList : IDynamicControl
{
    IList ListItems { get; set; }
}

我有一个返回IDynamicControlControlResolver类-出于我的目的,我有ASP的包装类。. NET文本框,复选框和下拉列表实现IDynamicControl(下拉列表实现IDynamicList)。要点是我可以给解析器一个控件的名称,例如"Textbox",它会给我返回一个iddynamiccontrol,这是一个修改过的ASP。网框。

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

string controlType = SomeService.GetControlType();
if (controlType == "dropdown")
{
    var control = (IDynamicList)ControlResolver.ResolveControl(controlType);
    // set up list items
}
else
{
    var control = ControlResolver.ResolveControl(controlType);
    // stuff with normal controls
}

但是这看起来很难看。我可以在基类中包含listtitems属性,然后在不使用它的类中抛出notimimplemented,但这违反了ISP,甚至比if语句更糟糕。简而言之,我想让我的解析器返回一种类型的控件,所以我不需要有不同的Resolve()方法,但在消费代码中有额外的工作,我必须做,当,只有当,该控件是一个下拉菜单。

除非我错了或者我忘记了一些基本的东西,有没有更好的解决方案来做这件事,或者我应该使用if语句?我不能使用基类,因为我所有的"动态"类都继承自基ASP。

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

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

你可以摆脱的一件事是有条件的if检查(想象一下在10种控制类型的情况下你需要多少if's),你可以使用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);

我认为重构原始代码的最简单方法是:

        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接口。

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

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

应该是这样的。你的ResolveControl方法提供了一个契约保证,它将返回一个iddynamiccontrol。它不保证将返回比此更具体的内容。

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

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

你希望调用者拥有所有的String方法和所有的Integer方法吗?

我建议,因为你知道你想要一个iddynamiclist你有一个解析器方法(ListControlResolver?)返回一个。也许它只是调用您现有的解析器并在返回之前强制转换它,但作为调用者的您并不关心这一点。哎呀,当您想要管理其他list-y控件时,这可能很有用。