当某些方法不会被使用/不实现时,使用接口还是抽象类?
我有一个项目,其中将抽象地定义相当多的函数和变量获取器。我的问题是我应该为此使用一个抽象类(每个函数都会抛出 NotImplementedException),还是应该使用一个接口?或者我应该同时使用两者,创建一个接口,然后创建一个实现该接口的抽象类?
请注意,即使可以定义所有这些函数等,但这并不意味着它们将在所有用例中使用。例如,身份验证类中的 AddUser 可能在接口中定义,但由于封闭的用户注册而从未在网站中使用。
I have a project where quite a few functions and variable getters will be defined, abstractly. My question is should I use an abstract class for this(with each function throwing NotImplementedException), or should I just use an interface? Or should I use both, making both an interface and then an abstract class implementing the interface?
Note, even though all of these functions and such may be defined, it does not mean they will all be used in all use cases. For instance, AddUser in an authentication class may be defined in an interface, but not ever used in a website due to closed user sign up.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
一般来说,要不要使用继承或接口的问题的答案可以这样思考:
例如,考虑
IEnumerable
接口。实现IEnumerable
的类都是不同的类。它们可以是一个可枚举的结构,但它们本质上是其他东西(List
或Dictionary
或查询等)另一方面,查看 System.IO.Stream 类。虽然从该抽象类继承的类不同(例如,
FileStream
与NetworkStream
),但它们本质上都是流,只是类型不同。流功能是定义这些类型的核心,而不是仅仅描述类型的一部分或它们提供的一组行为。通常你会发现两者都做是有益的;定义一个定义您的行为的接口,然后定义一个实现它并提供核心功能的抽象类。如果合适的话,这将使您能够两全其美:当功能是核心时可以继承一个抽象类,当功能不是核心时可以实现一个接口。
另外,请记住,仍然可以通过使用扩展方法在接口上提供一些核心功能。严格来说,虽然这并没有将任何实际的实例代码放在界面上(因为这是不可能的),但您可以模仿它。这就是 LINQ-to-Objects 查询函数通过静态
Enumerable
类在IEnumerable
上工作的方式,该类定义用于查询泛型的扩展方法>IEnumerable
实例。附带说明一下,您不需要抛出任何
NotImplementedException
。如果您将函数或属性定义为抽象,那么您不需要(实际上也不能)在抽象类中为其提供函数体;继承类将被迫提供方法体。 它们可能会抛出这样的异常,但这不是您需要担心的事情(对于接口也是如此)。In general, the answer to the question of whether or not to use inheritance or an interface can be answered by thinking about it this way:
Consider, for example, the
IEnumerable<T>
interface. The classes that implementIEnumerable<T>
are all different classes. They can be an enumerable structure, but they're fundamentally something else (aList<T>
or aDictionary<TKey, TValue>
or a query, etc.)On the other hand, look at the
System.IO.Stream
class. While the classes that inherit from that abstract class are different (FileStream
vs.NetworkStream
, for example), they are both fundamentally streams--just different kinds. The stream functionality is at the core of what defines these types, versus just describing a portion of the type or a set of behaviors that they provide.Often you'll find it beneficial to do both; define an interface that defines your behavior, then an abstract class that implements it and provides core functionality. This will allow you to, if appropriate, have the best of both worlds: an abstract class for inheriting from when the functionality is core, and an interface to implement when it isn't.
Also, bear in mind that it's still possible to provide some core functionality on an interface through the use of extension methods. While this doesn't, strictly speaking, put any actual instance code on the interface (since that's impossible), you can mimic it. This is how the LINQ-to-Objects query functions work on
IEnumerable<T>
, by way of the staticEnumerable
class that defines the extension methods used for querying genericIEnumerable<T>
instances.As a side note, you don't need to throw any
NotImplementedException
s. If you define a function or property asabstract
, then you don't need to (and, in fact, cannot) provide a function body for it within the abstract class; the inheriting classes will be forced to provide a method body. They might throw such an exception, but that's not something you need to worry about (and is true of interfaces as well).就我个人而言,我认为这取决于“类型”的定义。
如果您要定义一组行为,我会推荐一个接口。
另一方面,如果类型确实定义了“类型”,那么我更喜欢抽象类。不过,我建议让方法保持抽象,而不是提供空的行为。
如果这是真的,您应该考虑将其分解为多个抽象类或接口。在基类/接口中使用“不适当”的方法确实违反了里氏替换原则,以及设计缺陷的迹象。
Personally, I think it depends on what the "type" is defining.
If you're defining a set of behaviors, I would recommend an interface.
If, on the other hand, the type really defines a "type", then I'd prefer an abstract class. I would recommend leaving the methods abstract instead of providing an empty behavior, though.
If this is true, you should consider breaking this up into multiple abstract classes or interfaces. Having "inappropriate" methods in the base class/interface really is a violation of the Liskov Substitution Principle, and a sign of a design flaw.
如果您不提供任何实现,则使用接口,否则使用抽象类。如果有一些方法可能无法在子类中实现,那么创建一个中间抽象类来完成抛出 NotSupportedException 或类似的工作可能是有意义的。
If you're not providing any implementation, then use an interface otherwise use an abstract class. If there are some methods that may not be implemented in subclasses, it might make sense to create an intermediate abstract class to do the legwork of throwing NotSupportedException or similar.
抽象类的优点之一是,可以向抽象类添加新的类成员,其默认实现可以用现有的类成员来表示,而不会破坏该类的现有继承者。相比之下,如果向接口添加任何新成员,则必须修改该接口的每个实现以添加必要的功能。
如果 .net 允许接口包含不使用任何对象字段的属性、方法和事件的默认实现,那就太好了。从技术角度来看,我认为通过为每个接口提供一个默认 vtable 条目列表(可以与不定义所有 vtable 插槽的实现一起使用),可以毫不费力地完成这样的事情。不幸的是,.net 中不存在这样的能力。
One advantage of abstract classes is that one can add to an abstract class new class members whose default implementation can be expressed in terms of existing class members, without breaking existing inheritors of that class. By contrast, if any new members are added to an interface, every implementation of that interface must be modified to add the necessary functionality.
It would be very nice if .net allowed for an interface to include default implementations for properties, methods, and events which did not make any use of object fields. From a technical standpoint, I would think such a thing could be accomplished without too much difficulty by having for each interface a list of default vtable entries which could be used with implementations that don't define all vtable slots. Unfortunately, nothing like that ability exists in .net.
当您可以提供部分实现时,应该使用抽象类。当您根本不想提供任何实现(仅提供定义)时,请使用接口。
在你的问题中,听起来好像没有实现,所以使用接口。
另外,您应该使用
abstract
关键字声明您的方法/属性,而不是抛出NotImplementedException
,以便所有继承者都必须提供实现。Abstract classes should be used when you can provide a partial implementation. Use interfaces when you don't want to provide any implementation at all - just definition.
In your question, it sounds like there is no implementation, so go with an interface.
Also, rather than throwing
NotImplementedException
you should declare your method/property with theabstract
keyword so that all inheritors have to provide an implementation.@Earlz我认为参考这个:
注意,即使所有这些函数等都可能被定义,但这并不意味着它们都会在所有用例中使用。
与最佳方法直接相关来‘攻击’这个问题。您应该瞄准的是最大限度地减少此类函数的数量,以便在您使用或时它变得无关紧要(或至少不那么重要)。因此,尽可能地改进设计,您会发现走哪条路并不重要。
更好的是发布你正在尝试做的事情的高水平,让我们看看我们是否可以想出一些好的东西。更多的大脑朝着共同的目标努力将会得到更好的答案/设计。
@Earlz I think refering to this:
Note, even though all of these functions and such may be defined, it does not mean they will all be used in all use cases.
is directly related to the best way to 'attack' this problem.What you should aim at is minimizing the number of such functions so that it becomes irrelavant (or at least not that important) if you use either or. So improve the design as much as you can and you will see that it really doesn't matter which way you go.
Better yet post a high level of what you are trying to do and let's see if we can come up together with something nice. More brains working towards a common goal will get a better answer/design.
在某些情况下有效的另一种模式是创建一个非抽象的基类。它有一组定义 API 的公共方法。其中每一个都调用一个 Overideable 的受保护方法。
这允许派生类选择它需要实现的方法。
例如
Another pattern that works in some situations is to create a base class that is not abstract. Its has a set of public methods that define the API. Each of these calls a Protected method that is Overideable.
This allows the derived class to pick and choose what methods it needs to implement.
So for instance