C# 接口与继承:在这种情况下哪个更好?

C# 接口与继承:在这种情况下哪个更好?,c#,inheritance,interface,C#,Inheritance,Interface,假设我有一个小部件类: struct Widget { public Color Color { get; set; } public int Frobbles { get; set; } } 现在,我需要创建一个工厂来创建这些小部件,因此我构建了一个WidgetFactory: abstract class WidgetFactory { public virtual Widget GetWidget(); } class GoldWidgetFactory : W

假设我有一个小部件类:

struct Widget {
    public Color Color { get; set; }
    public int Frobbles { get; set; }
}
现在,我需要创建一个工厂来创建这些小部件,因此我构建了一个WidgetFactory:

abstract class WidgetFactory {
    public virtual Widget GetWidget();
}
class GoldWidgetFactory : WidgetFactory {
    public GoldWidgetFactory(GoldMine goldmine) {
        //...
    }

    public Widget GetWidget() {
        Gold g = goldmine.getGold();
        //...
    }
}

class XMLWidgetFactory : WidgetFactory {
    public XMLWidgetFactory(XmlDocument xmlsource) {
        //...
    }

    public Widget GetWidget() {
        XmlNode node = //whatever
        //...
    }
}

class MagicWidgetFactory : WidgetFactory {
    public Widget GetWidget() {
        //creates widget from nothing
    }
}
事实证明,您可以用几种不同的材料制作小部件,但生成的小部件基本相同。因此,我有一些WidgetFactory的实现:

abstract class WidgetFactory {
    public virtual Widget GetWidget();
}
class GoldWidgetFactory : WidgetFactory {
    public GoldWidgetFactory(GoldMine goldmine) {
        //...
    }

    public Widget GetWidget() {
        Gold g = goldmine.getGold();
        //...
    }
}

class XMLWidgetFactory : WidgetFactory {
    public XMLWidgetFactory(XmlDocument xmlsource) {
        //...
    }

    public Widget GetWidget() {
        XmlNode node = //whatever
        //...
    }
}

class MagicWidgetFactory : WidgetFactory {
    public Widget GetWidget() {
        //creates widget from nothing
    }
}
我的问题是:WidgetFactory应该是抽象类还是接口?我可以从两个方向看到论点:

基类:

  • 这些实现是WidgetFactorys
  • 他们可能能够共享功能(例如,一个
    列表WidgetFactory.GetAllWidgets()
    方法)
接口:

  • 这些实现不会从父级继承任何数据或功能
  • 它们的内部运作完全不同
  • 只定义了一个方法
对于那些回答的人来说,这(目前)与任何现实世界的问题都不平行,但是如果/当我需要实现这个模式时,最好知道。而且,“没关系”是一个正确的答案

编辑:我应该首先指出为什么要这样做。该类层次结构的假设用法如下:

//create a widget factory
WidgetFactory factory = new GoldWidgetFactory(myGoldMine);

//get a widget for our own nefarious purposes
Widget widget = factory.GetWidget();

//this method needs a few widgets
ConsumeWidgets(factory);

因此,在WidgetFactory中使用
GetGoldWidget()
方法不是一个好主意。另外,也许微件技术中的冒险可以让我们在未来添加不同的、更奇特的微件类型?添加一个新类来处理它们比将一个方法硬塞进一个现有类更容易、更干净。

您不需要继承、接口,甚至不需要多个类。单个工厂应该生产各种各样的小部件;只需将材质作为参数传递给create方法即可。其思想是向调用者隐藏对象不同构造的各个方面——通过创建一组不同的类,您将公开而不是隐藏它。

在这种情况下,我认为使用抽象类而不是接口没有任何好处

我通常倾向于接口而不是抽象类:

  • 他们不会在类继承中耗尽你的一次机会
  • 他们更容易被嘲笑
  • 它们在某种程度上感觉“更纯粹”(从接口就可以清楚地看出实现者需要提供什么;您不需要检查每个方法来确定它是具体的、抽象的还是虚拟的)
然而,在这种情况下,您可以很容易地使用委托,因为只有一个方法。。。基本上是一个
Func

我不同意Larry的想法,即只使用一个工厂直接创建具有单独方法的所有小部件-因为您可能希望将
WidgetFactory
作为依赖项传递给另一个不需要了解源代码的类,但需要在不同的时间或可能多次调用
CreateWidget


但是,您可以有一个具有多个方法的小部件工厂,每个方法都返回一个
Func
。这将带来拥有单个工厂类的好处,同时还允许“工厂”的依赖注入概念。

在您给出的示例中,
WidgetFactory
绝对没有理由成为抽象类,因为工厂的不同实现之间没有共享的属性或方法

即使有共享的功能,制作一个界面并将其传递给
WidgetFactory
的用户,以减少这些组件需要掌握的关于工厂的知识量,也会更加习惯

总体实现很好,而且确实是一个很好的解决方案,我唯一要做的是
IWidgetFactory

public interface IWidgetFactory {
    Widget GetWidget();
}

abstract class WidgetFactory : IWidgetFactory {
    //common attributes and methods
}

//Defferent implementations can still inherit from the base abstract class
class GoldWidgetFactory : WidgetFactory {
    public GoldWidgetFactory(GoldMine goldmine) {
        //...
    }

    public Widget GetWidget() {
        Gold g = goldmine.getGold();
        //...
    }
}

老实说,除了具体的工厂类之外,您还希望从WidgetFactory继承什么?任何东西曾经 如果不是,那可能永远都不重要。 如果将来您想在它们之间添加公共代码,那么最好选择抽象类。
此外,我并不认为工厂方法需要实现除创建方法之外的任何其他接口。因此,它是抽象的还是接口都不重要。这一切都取决于将来您是否希望在抽象类中添加额外的功能。

我已经在我的问题中添加了一条注释,说明了为什么这不是一个很好的答案。我认为您的第一句话是错误的-没有理由成为抽象类。因此,请澄清一下,我假设的
GetAllWidgets()
方法(我可能会通过调用GetWidget()直到没有了它,使用我刚刚发明的
WidgetAvailable
属性来实现)适合哪里?唯一的区别是我将IWidgetFactory设为泛型并将其约束到一个小部件,因此GetWidget将被正确键入。
GetAllWidgets
实际上不属于widget工厂。它实际上是一个独立的组件。类似于
WidgetFactoryCollection
类和
GetAllFactories
方法-本质上为您提供了一个可以使用的工厂列表。@Tom Anderson。约束基本工厂接口是没有意义的,因为您确实希望使用相同的方法从不同的工厂获取小部件?嗯,这是一个有趣的思考方式。不过,我不太擅长闭包,所以我倾向于工厂的离散实现……嗯。。。。兰姆达工厂。它可能非常适合我之前遇到的特定工厂问题……您不需要不同的方法名称——您可以使用相同的方法,并为不同的输入类型使用不同的方法覆盖该方法,例如GetWidget(Goldmine m)、GetWidget(Bronze b)等。