我应该使用继承吗?

发布于 2024-09-11 04:24:42 字数 623 浏览 7 评论 0原文

这更多的是一个主观问题,所以我将先将其标记为社区维基。

基本上,我发现在我的大多数代码中,有很多类,其中许多类相互使用,但很少有类彼此直接相关。我回顾我的大学时光,想到传统的 class Cat : Animal 类型示例,其中有巨大的继承树,但我在代码中没有看到这些。我的类图看起来像巨大的蜘蛛网,而不像漂亮的树。

我觉得我在逻辑上分离信息方面做得很好,最近我在通过 DI/IoC 技术隔离类之间的依赖关系方面做得很好,但我担心我可能会遗漏一些东西。我确实倾向于在接口中聚集行为,但我只是不进行子类化。

我可以根据传统示例轻松理解子类化,例如 class Dog : Animalclass Employee : Person,但我根本没有任何明显的东西处理。而且事情很少像class Label : Control那样清晰。但是,当涉及到将代码中的真实实体实际建模为层次结构时,我不知道从哪里开始。

所以,我想我的问题可以归结为:

  1. 不子类化或继承可以吗?我应该担心吗?
  2. 您必须采取哪些策略来确定可以从继承中受益的对象?
  3. 始终基于行为(接口)而不是实际类型进行继承是否可以接受?

This is more of a subjective question, so I'm going to preemptively mark it as community wiki.

Basically, I've found that in most of my code, there are many classes, many of which use each other, but few of which are directly related to each other. I look back at my college days, and think of the traditional class Cat : Animal type examples, where you have huge inheritance trees, but I see none of this in my code. My class diagrams look like giant spiderwebs, not like nice pretty trees.

I feel I've done a good job of separating information logically, and recently I've done a good job of isolating dependencies between classes via DI/IoC techniques, but I'm worried I might be missing something. I do tend to clump behavior in interfaces, but I simply don't subclass.

I can easily understand subclassing in terms of the traditional examples such as class Dog : Animal or class Employee : Person, but I simply don't have anything that obvious I'm dealing with. And things are rarely as clear-cut as class Label : Control. But when it comes to actually modeling real entities in my code as a hierarchy, I have no clue where to begin.

So, I guess my questions boil down to this:

  1. Is it ok to simply not subclass or inherit? Should I be concerned at all?
  2. What are some strategies you have to determine objects that could benefit from inheritance?
  3. Is it acceptable to always inherit based on behavior (interfaces) rather than the actual type?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

淡写薰衣草的香 2024-09-18 04:24:43

继承应该始终代表“is-a”关系。如果 A 派生自 B,您应该能够说“A 是 B”。如果不是,则更喜欢组合。没有必要时不进行子类化是完全可以的。

例如,说 FileOpenDialog "is-a" Window 有意义,但说 Engine "is-a" Car 纯属无稽之谈。在这种情况下,Car 实例中的 Engine 实例更合适(可以说 Car “is-implemented-in-条款“引擎)。

有关继承的详细讨论,请参阅第 1 部分gotw.ca 上“继承的使用和滥用”的第 2 部分

Inheritance should always represent an "is-a" relationship. You should be able to say "A is a B" if A derives from B. If not, prefer composition. It's perfectly fine to not subclass when it is not necessary.

For example, saying that FileOpenDialog "is-a" Window makes sense, but saying that an Engine "is-a" Car is nonsense. In that case, an instance of Engine inside a Car instance is more appropriate (It can be said that Car "is-implemented-in-terms-of" Engine).

For a good discussion of inheritance, see Part 1 and Part 2 of "Uses and Abuses of Inheritance" on gotw.ca.

薆情海 2024-09-18 04:24:43
  1. 只要你不遗漏明确的“is a”关系,就可以,事实上,最好不要继承,而是使用组合。

  2. is-a 是试金石。 if (X 是 Y 吗?) then class X : Y { } else class X { Y myY; }class Y { X myX; }

  3. 使用接口,即继承行为,是一种非常简洁的代码结构方式,只需添加所需的行为而不添加其他行为。棘手的部分是很好地定义这些接口。

  1. As long as you do not miss the clear cut 'is a' relationships, it's ok and in fact, it's best not to inherit, but to use composition.

  2. is-a is the litmus test. if (Is X a Y?) then class X : Y { } else class X { Y myY; } or class Y { X myX; }

  3. Using interfaces, that is, inheriting behavior, is a very neat way to structure the code via adding only the needed behavior and no other. The tricky part is defining those interfaces well.

花想c 2024-09-18 04:24:43

任何技术或模式都不应该为了自身的目的而使用。显然,您工作的领域中的类往往无法从继承中受益,因此您不应该使用继承。

您已经使用 DI 来保持一切整洁和干净。您分离了班级的关注点。这些都是好东西。如果您并不真正需要继承,请不要尝试强制继承。

这个问题的一个有趣的后续问题是:哪些编程领域确实倾向于充分利用继承? (UI 和数据库框架已经被提及,都是很好的例子。还有其他的吗?)

No technology or pattern should be used for its own sake. You obviously work in a domain where classes tend to not benefit from inheritance, so you shouldn't use inheritance.

You've used DI to keep things neat and clean. You separated the concerns of your classes. Those are all good things. Don't try and force inheritance if you don't really need it.

An interesting follow-up to this question would be: Which programming domains do tend to make good use of inheritance? (UI and db frameworks have already been mentioned and are great examples. Any others?)

猫九 2024-09-18 04:24:43

我也讨厌狗->哺乳动物->动物的例子,正是因为它们在现实生活中不会发生。

我很少使用子类化,因为它将子类与超类紧密耦合,并使您的代码非常难以阅读。有时实现继承是有用的(例如 PostgreSQLDatabaseImpl 和 MySQLDatabaseImpl 扩展 AbstractSQLDatabase),但大多数时候它只会让事情变得一团糟。大多数时候,我看到子类这个概念被误用了,应该使用接口或属性。

然而,接口很棒,您应该使用它们。

I also hate the Dog -> Mammal -> Animal examples, precisely because they do not occur in real life.

I use very little subclassing, because it tightly couples the subclass to the superclass and makes your code really hard to read. Sometimes implementation inheritance is useful (e.g. PostgreSQLDatabaseImpl and MySQLDatabaseImpl extend AbstractSQLDatabase), but most of the time it just makes a mess of things. Most of the time I see subclasses the concept has been misused and either interfaces or a property should be used.

Interfaces, however, are great and you should use those.

递刀给你 2024-09-18 04:24:43

一般来说,倾向于组合而不是继承。 继承往往会破坏封装。例如,如果一个类依赖于超类的方法,并且超类在某个版本中更改了该方法的实现,则子类可能会崩溃。

有时,当您设计框架时,您将设计要继承的类。如果您想使用继承,则必须仔细记录和设计它。例如,在构造函数中不调用任何实例方法(可能被子类覆盖)。此外,如果它是真正的“is-a”关系,继承很有用,但如果在包中使用,继承会更强大。

请参阅Effective Java(第 14 项和第 15 项)。它为为什么你应该支持组合而不是继承提供了一个很好的论据。它总体上讨论了继承和封装(带有 java 示例)。因此,即使您不使用 java,它也是一个很好的资源。

因此,回答您的 3 个问题:

简单地不子类化或继承可以吗?我应该担心吗?
答:问问自己这个问题是否是真正的“is-a”关系?装修可以吗?进行装饰

// A collection decorator that is-a collection with 
public class MyCustomCollection implements java.util.Collection {
    private Collection delegate;
    // decorate methods with custom code
}

您必须采取哪些策略来确定可以从继承中受益的对象?
Ans:通常,当您编写框架时,您可能希望提供专门为继承而设计的某些接口和“基”类。

始终基于行为(接口)而不是实际类型进行继承是否可以接受?
答:大多数情况下是的,但是如果超类是为继承而设计的和/或在您的控制之下,您会更好。或者去作曲。

Generally, favour composition over inheritance. Inheritance tends to break encapsulation. e.g. If a class depends on a method of a super class and the super class changes the implementation of that method in some release, the subclass may break.

At times when you are designing a framework, you will have to design classes to be inherited. If you want to use inheritance, you will have to document and design for it carefully. e.g. Not calling any instance methods (that could be overridden by your subclasses) in the constructor. Also if its a genuine 'is-a' relationship, inheritance is useful but is more robust if used within a package.

See Effective Java (Item 14, and 15). It gives a great argument for why you should favour composition over inheritance. It talks about inheritance and encapsulation in general (with java examples). So its a good resource even if you are not using java.

So to answer your 3 questions:

Is it ok to simply not subclass or inherit? Should I be concerned at all?
Ans: Ask yourself the question is it a truly "is-a" relationship? Is decoration possible? Go for decoration

// A collection decorator that is-a collection with 
public class MyCustomCollection implements java.util.Collection {
    private Collection delegate;
    // decorate methods with custom code
}

What are some strategies you have to determine objects that could benefit from inheritance?
Ans: Usually when you are writing a framework, you may want to provide certain interfaces and "base" classes specifically designed for inheritance.

Is it acceptable to always inherit based on behavior (interfaces) rather than the actual type?
Ans: Mostly yes, but you'd be better off if the super class is designed for inheritance and/or under your control. Or else go for composition.

少钕鈤記 2024-09-18 04:24:43

恕我直言,你永远不应该做#3,除非你专门为此目的构建一个抽象基类,并且它的名称清楚地表明了它的目的是什么:

class DataProviderBase {...}
class SqlDataProvider : DataProviderBase {...}
class DB2DataProvider : DataProviderBase {...}
class AccountDataProvider : SqlDataProvider {...}
class OrderDataProvider : SqlDataProvider {...}
class ShippingDataProvider : DB2DataProvider {...}

等等。

也遵循这种类型的模型,有时如果你提供一个接口( IDataProvider)最好还提供一个基类(DataProviderBase),未来的消费者可以使用它来方便地访问应用程序模型中所有/大多数 DataProvider 所共有的逻辑。

不过,作为一般规则,我仅在具有真正的“is-a”关系时才使用继承,或者如果继承可以改善我创建“is-a”关系的整体设计(例如,提供者模型。)

IMHO, you should never do #3, unless you're building an abstract base class specifically for that purpose, and its name makes it clear what its purpose is:

class DataProviderBase {...}
class SqlDataProvider : DataProviderBase {...}
class DB2DataProvider : DataProviderBase {...}
class AccountDataProvider : SqlDataProvider {...}
class OrderDataProvider : SqlDataProvider {...}
class ShippingDataProvider : DB2DataProvider {...}

etc.

Also following this type of model, sometimes if you provide an interface (IDataProvider) it's good to also provide a base class (DataProviderBase) that future consumers can use to conveniently access logic that's common to all/most DataProviders in your application model.

As a general rule, though, I only use inheritance if I have a true "is-a" relationship, or if it will improve the overall design for me to create an "is-a" relationship (provider model, for instance.)

九歌凝 2024-09-18 04:24:43

在具有共享功能的情况下,对接口进行编程比继承更重要。

本质上,继承更多的是关于将对象关联在一起。

大多数时候,我们关心的是一个对象可以做什么,而不是它是什么。

class Product
class Article
class NewsItem

NewsItemArticle 都是 Content 项吗?也许,您可能会发现能够拥有包含 Article 项和 NewsItem 项的内容列表很有用。

但是,您更有可能让它们实现类似的接口。例如,IRssFeedable 可以是它们都实现的接口。事实上,Product也可以实现这个接口。

然后,它们都可以轻松地扔到 RSS 源中,以在您的网页上提供内容列表。当接口很重要而继承模型可能不太有用时,这是一个很好的例子。

继承就是识别对象的本质
接口就是为了识别对象可以做什么。

Where you have shared functionality, programming to the interface is more important than inheritance.

Essentially, inheritance is more about relating objects together.

Most of the time we are concerned with what an object can DO, as opposed to what it is.

class Product
class Article
class NewsItem

Are the NewsItem and Article both Content items? Perhaps, and you may find it useful to be able to have a list of content which contains both Article items and NewsItem items.

However, it's probably more likely you'll have them implement similar interfaces. For example, IRssFeedable could be an interface that they both implement. In fact, Product could also implement this interface.

Then they can all be thrown to an RSS Feed easily to provide lists of things on your web page. This is a great example when the interface is important whereas the inheritance model is perhaps less useful.

Inheritance is all about identifying the nature of Objects
Interfaces are all about identifying what Objects can DO.

倥絔 2024-09-18 04:24:43

我的类层次结构也往往相当平坦,接口和组合提供了必要的耦合。当我存储事物的集合时,继承似乎主要出现,其中不同种类的事物将具有共同的数据/属性。当存在公共数据时,继承对我来说通常感觉更自然,而接口是表达公共行为的一种非常自然的方式。

My class hierarchies tend to be fairly flat as well, with interfaces and composition providing the necessary coupling. Inheritance seems to pop up mostly when I'm storing collections of things, where the different kinds of things will have data/properties in common. Inheritance often feels more natural to me when there is common data, whereas interfaces are a very natural way to express common behavior.

独﹏钓一江月 2024-09-18 04:24:43

您的 3 个问题的答案都是“视情况而定”。最终,这完全取决于您的域以及您的程序用它做什么。很多时候,我发现我选择使用的设计模式实际上有助于找到继承发挥作用的点。

例如,考虑一个用于将数据转换为所需形式的“转换器”。如果您以 CSV 文件形式获取 3 个数据源,并希望将它们放入三个不同的对象模型中(并且可能将它们保存到数据库中),您可以创建一个“csv 转换器”基础,然后在继承它时重写一些方法为了处理不同的特定对象。

将开发过程“投射”到模式语言中将帮助您找到行为相似的对象/方法,并有助于减少冗余代码(也许通过继承,也许通过使用共享库 - 无论哪种最适合情况)。

另外,如果您将各层保持独立(业务、数据、表示等),您的类图将会更简单,然后您可以“可视化”那些需要继承的对象。

The answer to each of your 3 questions is "it depends". Ultimately it will all depend on your domain and what your program does with it. A lot of times, I find the design patterns I choose to use actually help with finding points where inheritance works well.

For example, consider a 'transformer' used to massage data into a desired form. If you get 3 data sources as CSV files, and want to put them into three different object models (and maybe persist them into a database), you could create a 'csv transformer' base and then override some methods when you inherit from it in order to handle the different specific objects.

'Casting' the development process into the pattern language will help you find objects/methods that behave similarly and help in reducing redundant code (maybe through inheritance, maybe through the use of shared libraries - whichever suits the situation best).

Also, if you keep your layers separate (business, data, presentation, etc.), your class diagram will be simpler, and you could then 'visualize' those objects that aught to be inherited.

梦冥 2024-09-18 04:24:43

我不会太担心你的类图的外观,事情很少像教室一样......

而是问自己两个问题:

  1. 你的代码有效吗?

  2. 维护起来是否非常耗时?有时更改是否需要在许多地方更改“相同”代码?

如果(2)的答案是肯定的,您可能想看看如何构建代码,看看是否有更明智的方式,但始终记住,最终,您需要能够对问题(1)回答是...漂亮的代码不能工作对任何人都没有用,并且很难向管理层解释。

I wouldn't get too worried about how your class diagram looks, things are rarely like the classroom...

Rather ask yourself two questions:

  1. Does your code work?

  2. Is it extremely time consuming to maintain? Does a change sometimes require changing the 'same' code in many places?

If the answer to (2) is yes, you might want to look at how you have structured your code to see if there is a more sensible fashion, but always bearing in mind that at the end of the day, you need to be able to answer yes to question (1)... Pretty code that doesn't work is of no use to anybody, and hard to explain to the management.

み格子的夏天 2024-09-18 04:24:43

恕我直言,使用继承的主要原因是允许编写用于操作基类对象的代码来操作派生类对象。

IMHO, the primary reason to use inheritance is to allow code which was written to operate upon a base-class object to operate upon a derived-class object instead.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文