接口和类继承。这是一个可以避免的模式吗?

时间:2011-05-24 16:41:33

标签: c# code-analysis

在以下场景中,我对以何种方式继续提出建议表示感谢。让我们看看我是否可以清楚地解释它(英语不是我的母语,所以事情可能会让人感到困惑,对不起)。

假设我有以下接口:

internal interface IBlah
{
     int Frob();
}

internal interface IBlahOnSteroids: IBlah
{
     double Duh();
}

现在我们有一个与IBlah对象有'has'关系的Foo类:

public class Foo
{
     IBlah blah;

     internal Foo(IBlah blah)
     {
          this.blah = blah;
     }

     public int Frob()
     {
          ....
          return this.blah.Frob();
     }
}

现在我们还需要一个与IBlahOnSteroids对象具有'has'关系的FooOnSteroids类。 问题是,知道IBlahOnSteroids的一部分已经在Foo中实现了,如果我们创建了会发生什么 FooOnSteroids继承自Foo?

我们会得到这样的结果:

public class FooOnSteroids: Foo
{
    IBlahOnSteroids blah;

    internal FooOnSteroids(IBlahOnSteroids blah)
        :base(blah) 
    {
         this.blah = blah;
    }

    public double Duh()
    {
         return this.blah.Duh();
    }
}

这是推荐的模式吗?我们将继承链传递给相同的'blah'对象,并且在每个“级别”我们将它存储在私有中 具有“有用”类型的字段。我可以看到,我无法在BlahBase中存储受保护的属性 暴露了一个常见的IBlah参考所有下降类,因为它必须是IBlah类型,对BlahOnSteroids没用。这种情况是否均匀 推荐的?或者我们应该只将Foo和FooOnSteroids实现为没有继承的独立类(这会产生代码重复)?也许它绝对可以做到这一点,但它不知何故感觉像一个黑客。是吗?

使用泛型的选项,可以立即解决问题,这是不可能的,因为我知道这很糟糕,这个库必须以.Net 1.x平台为目标。

只是实现BlahOnSteroids的选项也是可能的,但这意味着取决于调用者,我们将不得不抛出异常,如果有的话 IBlahOnSteroids成员被召唤。我不喜欢那样。

非常感谢任何建议!

4 个答案:

答案 0 :(得分:2)

一种替代模式是

public class Foo
{
    protected IBlah Blah { get; private set; }
    ...
}
public class FooOnSteroids : Foo 
{
    private new IBlahOnSteroids Blah { get { return (IBlahOnSteroids)base.Blah; } }
    ...
}

但是,这与您的代码没有太大区别;如果你不能使用泛型,它们都很好。

答案 1 :(得分:2)

您可以通过使基本字段可用来减少重复:

IBlah blah;
protected IBlah Blah { get { return blah; } }

并在子类型中进行投射(因为您希望您选择blah得到尊重):

public double Duh() {
    return ((IBlahOnSteroids)Blah).Duh();
}

你也可以在基类型上做泛型(避免演员),但我不确定它是否值得。但请注意,如果基类决定注入装饰器blah(如果装饰器不提供第二个接口),则可能会爆炸。

答案 2 :(得分:1)

在回答你关于删除Foo和FooOnSteroids之间继承的问题时,我不知道你的所有推理,但我可以尝试提供一些一般性的指导。您应该考虑使用继承主要是为了让您的客户能够使用FooOnSteroids实例,但只编写Foo的代码。

因此,如果从概念上讲,您的客户会这样做:

Foo foo = new FooOnSteroids();
foo.Frob()

你应该保留继承权。

如果您只是为了重用代码而创建继承关系,我建议您考虑重构类以包含提供共享功能的类。继承不是代码重用的最佳模式。

答案 3 :(得分:1)

鉴于您不能使用泛型(可能会或可能不会使您的特定用例更容易),我个人会选择投射blah成员Foo。使用C#进行投射相对无痛:

public double Duh()
{
    return (this.blah as IBlahOnSteroids).Duh();
}

如果无法将对象强制转换为您请求的类型,则C#中的as关键字将评估为null。在上面的示例中,如果this.blah不是IBlahOnSteroids的实例,您将获得NullReferenceException。您可以检查对象是否是类似的实例:

public double Duh()
{
    if (this.blah is IBlahOnSteroids)
        return (this.blah as IBlahOnSteroids).Duh();
    else
        throw new InvalidTypeException("Blah is not an instance of IBlahOnSteroids");
}

虽然在原始示例的代码中,blah的{​​em>可能不应该是IBlahOnSteroids的实例,因为它是在构造函数中分配的,这使得在编译时为你断言。