你如何设计继承类? [英] How do you design a class for inheritance?

查看:183
本文介绍了你如何设计继承类?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我听说它说设计继承是困难的,但我从来没有发现是这样的。任何人(和任何人,我的意思是Jon Skeet)解释为什么这被称为困难,陷阱/障碍/问题是什么,为什么只是凡人程序员不应该尝试它,只是让他们的课密封来保护无辜者?好吧,我小时候谈到后者 - 但是我很好奇,看看有没有人(包括乔恩)真的有难度的设计继承。我真的从来没有考虑过这个问题,但也许我忽视了我认为是理所当然的事情 - 或者在没有意识到的情况下拧紧东西!



编辑:感谢所有的优秀到目前为止的答案。我相信共识是​​,对于典型的应用程序类(WinForm子类,一次性实用程序类等),不需要考虑任何类型的重用,更不用说通过继承重用,而对于库类,重要的是考虑重用通过设计中的继承。



我真的没有想到,WinForm类实现一个GUI对话框,作为一个可能重用的类 - 我排序认为它是一个一次性的对象。但在技术上它是一个类,有人可能会从它继承 - 但它不是很可能。



我已经做了很多大规模的开发基础库和框架的库,所以设计通过继承重用是至关重要的 - 我从来没有认为这是困难,只是。 ; - )



但是,与WinForms等常见应用程序任务的一次性课程相比,我也从未考虑过。



当然,欢迎设计继承的更多提示和缺陷;我也会尝试扔掉一些。

解决方案

而不是重复它太多,我只会回答你应该看看我的问题在对java.util.Properties进行子类化时。我倾向于尽可能避免许多继承设计的问题。这里有一些问题的想法:




  • 正确执行平等是一种痛苦(或者是不可能的),除非你将其限制为两个对象必须具有完全相同的类型。困难来自对称要求, a.Equals(b)意味着 b.Equals(a)。如果 a b 是10x10平方和10x 10分别,然后 a 可能会认为 b 等于它 - 而可能在许多情况下,您实际想要测试。但是 b 知道它有一个颜色,而 a 不...


  • 每当您在实现中调用虚拟方法时,都必须记录该关系,从不改变它。从类派生的人也需要读取该文档,否则它们可能会以其他方式实现该调用,迅速导致调用X调用X的X调用Y的堆栈溢出。这是我的主要问题 - 在许多方面您必须记录您的实现的情况,导致缺乏灵活性。如果你从来没有从另一个人那里调用一个虚拟方法,那么它会被显着的减轻,但是你仍然需要对虚拟方法进行任何其他的调用,甚至是从非虚拟方法调用,也不要在这个意义上改变实现。 / p>


  • 即使没有一些未知代码是执行时间类的一部分,线程安全性也很难实现。您不仅需要记录您的类的线程模型,而且您可能还必须将派生类的锁(等)公开,以便以同样的方式进行线程安全。


  • 在保持原始基类的合同期间,考虑什么样的专业化是有效的。方法可以以什么方式被覆盖,以便调用者不需要知道专业化,只是它的工作原理? java.util.Properties是一个很好的例子,这里的不好的专业化 - 调用者不能把它当成一个正常的Hashtable,因为它应该只有键和值的字符串。


  • 如果一个类型是不可变的,它不应该允许继承 - 因为一个子类可以很容易地是可变的。如果你实现某种克隆能力,那么恶作剧可能会很多。


  • 如果没有,可能会使子类更难以正确克隆。有时一个成员克隆是够好的,但其他时候可能没有意义。


  • 如果你不提供任何虚拟成员,你可能是相当安全的 - 但在这一点上,任何子类都提供额外的,不同的功能,而不是专门化现有的功能。这不是一件坏事,但它并不像原来的继承目的。




与图书馆设计师相比,许多这些应用程序构建者的问题不仅仅是一个问题:如果你知道你和你的同事将是唯一从你的类型中获得的人,那么你可以摆脱一个很少的前期设计 - 你可以修复子类,如果你需要在稍后的日期。



顺便说一下,这些只是袖珍点。如果我认为足够长的时间,我可能会想出更多的东西。 Josh Bloch将它放在有效的Java 2 btw中。


I've heard it said that it is "difficult" to "design for inheritance", but I've never found that to be the case. Can anyone (and by anyone, I mean Jon Skeet) explain why this is allegedly difficult, what the pitfalls/obstacles/issues are, and why mere mortal programmers should not attempt it and just make their classes sealed to protect the innocent?

ok, i kid about the latter - but i am curious to see if anyone (including Jon) really has difficulties "designing for inheritance". I have really never considered it an issue but perhaps I am overlooking something that I take for granted - or screwing something up without realizing it!

EDIT: thanks for all the excellent answers so far. I believe the consensus is that for typical application classes (WinForm subclasses, one-off utility classes, et al) there is no need to consider reuse of any kind, much less reuse via inheritance, while for library classes it is critical to consider reuse via inheritance in the design.

I don't really think about, say, a WinForm class to implement a GUI dialog, as a class that someone might reuse - I sort of think of it as a one-off object. But technically it is a class and someone might inherit from it - but it's not very likely.

A lot of the larger-scale development that I've done has been class libraries for base libraries and frameworks, so designing for reuse by inheritance was critical - I just never considered it to be "difficult", it just was. ;-)

But I also never considered it in contrast to the 'one-off' classes for common application tasks like WinForms et al.

More tips and pitfalls of designing for inheritance are welcome, of course; I'll try to throw in some, too.

解决方案

Rather than rehashing it too much, I'll simply answer that you should have a look at the problems I had when subclassing java.util.Properties. I tend to avoid many of the problems of designing for inheritance by doing so as rarely as possible. Here are a few ideas of the problems though:

  • It's a pain (or potentially impossible) to implement equality properly, unless you limit it to "both objects must have exactly the same type". The difficulty comes with the symmetric requirement that a.Equals(b) implies b.Equals(a). If a and b are "a 10x10 square" and "a red 10x10 square" respectively, then a might well think that b is equal to it - and that may be all you actually want to test for, in many cases. But b knows that it has a colour and a doesn't...

  • Any time you call a virtual method in your implementation, you've got to document that relationship, and never, ever change it. The person deriving from the class needs to read that documentation, too - otherwise they might implement the call the other way round, quickly leading to a stack overflow of X calling Y which calls X which calls Y. This is my main problem - in many cases you have to document your implementation which leads to a lack of flexibility. It's mitigated significantly if you never call one virtual method from another, but you still have to document any other calls to virtual methods, even from non-virtual ones, and never change the implementation in that sense.

  • Thread safety is hard to achieve even without some unknown code being part of your execution-time class. You not only need to document the threading model of your class, but you may also have to expose locks (etc) to derived classes so they can be thread-safe in the same way.

  • Consider what sort of specialization is valid while keeping within the contract of the original base class. In what ways can methods be overridden such that a caller shouldn't need to know about the specialization, just that it works? java.util.Properties is a great example of bad specialization here - callers can't just treat it as a normal Hashtable, because it should only have strings for keys and values.

  • If a type is meant to be immutable, it shouldn't allow inheritance - because a subclass can easily be mutable. Oddities could then abound.

  • Should you implement some sort of cloning ability? If you don't, it may make it harder for subclasses to clone properly. Sometimes a memberwise clone is good enough, but other times it may not make sense.

  • If you don't provide any virtual members, you may well be reasonably safe - but at that point any subclasses are providing extra, different functionality rather than specializing the existing functionality. That's not necessarily a bad thing, but it doesn't feel like the original purpose of inheritance.

Many of these are much less of a problem for application builders than class library designers: if you know that you and your colleagues are going to be the only people ever to derive from your type, then you can get away with a lot less up-front design - you can fix the subclasses if you need to at a later date.

These are just off-the-cuff points, by the way. I may be able to come up with more if I think for long enough. Josh Bloch puts it all very well in Effective Java 2, btw.

这篇关于你如何设计继承类?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆