阻止课程继承的原因是什么? (例如在c#类上使用密封) 现在我想不出任何。
答案 0 :(得分:21)
因为编写可替代扩展的类是该死的,并要求您准确预测未来用户将如何扩展您所写的内容。
密封你的类会迫使他们使用更强大的构图。
答案 1 :(得分:9)
如果您还不确定界面并且根据当前界面不需要任何其他代码,那怎么样? [这是我的头脑,但我也对其他原因感兴趣!]
编辑: 一些谷歌搜索给出了以下内容: http://codebetter.com/blogs/patricksmacchia/archive/2008/01/05/rambling-on-the-sealed-keyword.aspx
引用:
密封类比未密封类更好的原因有三个:
答案 2 :(得分:8)
我想从“代码完成”中给你这条消息:
继承 - 子类 - 倾向于 反对主要技术 你作为一名程序员必不可少的 这是为了管理复杂性。为了控制复杂性,你应该对继承保持沉重的偏见。
答案 3 :(得分:2)
继承的唯一合法用法是定义基类的特定情况,例如,从Shape继承以派生Circle。要检查这个相反方向的关系:是一个Circle的泛化吗?如果答案是肯定的,那么可以使用继承。
因此,如果你有一个课程,不能有任何特殊情况专门化它的行为,它应该被密封。
同样由于LSP(Liskov替换原则),可以使用派生类,其中基类是预期的,这实际上对继承的使用产生了最大的影响:使用基类的代码可以被赋予一个继承的类,它仍然必须按预期工作。为了在没有明显需要子类的情况下保护外部代码,您可以密封该类,并且其客户端可以依赖其行为不会被更改。否则,需要明确设计外部代码以期望子类中行为的可能变化。
更具体的例子是Singleton模式。你需要密封单身,以确保不会打破“单身”。
答案 4 :(得分:1)
这可能不适用于您的代码,但.NET框架中的许多类都是故意密封的,因此没有人尝试创建子类。
在某些情况下,内部结构很复杂,并且需要非常具体地控制某些事情,因此设计者决定不应该继承该类,以便通过错误的方式使用某些东西而不会意外地破坏功能。
答案 5 :(得分:1)
@jjnguy
另一个用户可能希望通过对您的类进行子类化来重用您的代码。我没有理由阻止这一点。
如果他们想要使用我的类的功能,他们可以通过包含来实现这一功能,并且因此它们将具有更少的脆弱代码。
组成似乎经常被忽视;很多时候人们都希望跳上继承的潮流。他们不应该!可替代性很难。默认为组合;从长远来看,你会感谢我。
答案 6 :(得分:1)
我同意jjnguy ......我认为封印课程的原因很少而且很远。恰恰相反,我不止一次处于这种情况,我想延长课程,但不能因为它被密封了。
作为一个完美的例子,我最近创建了一个小包(Java,而不是C#,但原理相同)来围绕memcached工具包装功能。我想要一个接口,所以在测试中我可以模拟我正在使用的memcached客户端API,并且如果需要的话我们也可以切换客户端(memcached主页上列出了2个客户端)。此外,如果出现需求或愿望,我希望有机会完全替换功能(例如,如果memcached服务器由于某种原因而关闭,我们可能会使用本地缓存实现进行热交换)。
我公开了一个与客户端API交互的最小接口,扩展客户端API类然后只需用我的新接口添加implements子句就太棒了。我在接口中与实际接口匹配的方法将不需要进一步的细节,因此我不必明确地实现它们。但是,这个类是密封的,所以我不得不代理调用这个类的内部引用。结果:更多的工作和更多的代码没有真正的理由。
那就是说,我认为有可能你想要让一个类密封......我能想到的最好的事情是你将直接调用的API,但允许客户实现。例如,您可以针对游戏进行编程的游戏......如果您的课程没有密封,那么添加功能的玩家可能会利用API来获得优势。这是一个非常狭窄的情况,我认为只要你完全控制代码库,就没有什么理由让一个类密封。
这就是我真正喜欢Ruby编程语言的一个原因......即使核心类是开放的,不仅仅是为了扩展而是动态地添加和更改功能,自始至终!它被称为monkeypatching,如果被滥用可能是一场噩梦,但玩起来真是太棒了!
答案 7 :(得分:1)
从面向对象的角度来看,密封课程清楚地记录了作者的意图而无需评论。当我封印课程时,我试图说这个课程的目的是封装一些特定的知识或某些特定的服务。它并不意味着进一步增强或继承。
这适用于模板方法设计模式。我有一个界面说“我执行此服务”。然后我有一个实现该接口的类。但是,如果执行该服务依赖于基类不知道(并且不应该知道)的上下文,该怎么办?会发生什么是基类提供虚拟方法,这些方法是受保护的或私有的,并且这些虚方法是子类的钩子,用于提供基类不知道和不知道的信息或动作。同时,基类可以包含所有子类共有的代码。这些子类将被密封,因为它们旨在实现该服务的唯一一个具体实现。
您能否提出这些子类应进一步子类化以增强它们的论点?我会说不,因为如果那个子类无法在第一时间完成工作,那么它应该永远不会从基类派生出来。如果您不喜欢它,那么您拥有原始界面,请编写您自己的实现类。
密封这些子类也会阻碍深层次的继承,这对GUI框架很有效,但对业务逻辑层的效果很差。
答案 8 :(得分:0)
因为你总是希望因各种原因而被提交给班级而不是派生班级:
一世。您在代码的其他部分中使用的不变量
II。安全
等
另外,因为对于向后兼容性来说这是一个安全的选择 - 如果它的释放未密封,你永远无法关闭该类继承。
或者您可能没有足够的时间来测试该类公开的接口,以确保您可以允许其他人继承该接口。
或者也许没有必要(你现在看到)有一个子类。
或者,当人们尝试创建子类并且无法获得所有细节时,您不需要错误报告 - 削减支持成本。
答案 9 :(得分:0)
有时你的班级界面并不意味着被激活。公共接口不是虚拟的,虽然有人可以覆盖现有的功能,但这只是错误的。是的,一般来说,它们不应该覆盖公共接口,但是你可以通过使类不可继承来确保它们不会。
我现在能想到的例子是自定义包含.Net中具有深度克隆的类。如果你从他们那里继承你就失去了深刻的克隆能力。[我在这个例子上有点模糊,自从我使用IClonable已经有一段时间]如果你有一个真正的singelton类,你可能不希望继承的形式它周围,数据持久层通常不是你想要大量继承的地方。
答案 10 :(得分:0)
并非所有在类中重要的东西都很容易在代码中断言。存在可以通过继承和重写方法容易地破坏的语义和关系。一次覆盖一个方法是一种简单的方法。你将一个类/对象设计为一个有意义的实体,然后有人出现并认为一个或两个方法“更好”它不会造成伤害。这可能是也可能不是。也许你可以正确地区分私有和非私有或虚拟之间的所有方法,而不是虚拟但仍然可能还不够。要求所有类的继承也给原始开发人员带来了巨大的额外负担,以预见继承类可以搞砸的所有方式。
我不知道一个完美的解决方案。我同情防止继承,但这也是一个问题,因为它阻碍了单元测试。
答案 11 :(得分:0)
我公开了一个与客户端API交互的最小接口,扩展客户端API类然后只需用我的新接口添加implements子句就太棒了。我在接口中与实际接口匹配的方法将不需要进一步的细节,因此我不必明确地实现它们。但是,这个类是密封的,所以我不得不代理调用这个类的内部引用。结果:更多的工作和更多的代码没有真正的理由。
嗯,有一个原因:您的代码现在与memcached接口的更改有些隔离。
答案 12 :(得分:0)
性能:(...)如果JIT编译器看到使用密封类型调用虚方法,JIT编译器可以通过非虚拟地调用该方法来生成更高效的代码。(...)
这确实是一个很好的理由。因此,对于性能关键的类,sealed
和朋友是有道理的。
到目前为止,我所见到的所有其他原因归结为“没有人接触我的班级!”。如果你担心有人可能误解了它的内部结构,那你记录下来的工作很糟糕。您不可能知道添加到您的课程中没有任何用处,或者您已经知道它的每个可以想象的用例。即使你是对的,而另一个开发人员不应该使用你的类来解决他们的问题,使用关键字也不是防止这种错误的好方法。文档是。如果他们忽略了文件,他们的损失。
答案 13 :(得分:0)
大多数答案(抽象时)表明密封/最终确定的类是保护其他程序员免受潜在错误的工具。有意义的保护和无意义的限制之间存在着模糊的界限。但只要程序员是理解该程序的人,我就几乎没有理由限制他重复使用部分课程。大多数人都在谈论课程。但这都是关于物体的!
在他的第一篇文章中,DrPizza声称设计可继承的类意味着预期可能的扩展。我是否认为只有在可能扩展好的课程时才能继承这个课程?看起来好像你习惯于从最抽象的类中设计软件。请允许我简要解释一下设计时的想法:
从非常具体的对象开始,我找到了它们共有的特征和[因此]功能,并将它抽象为那些特定对象的超类。这是一种减少代码重复性的方法。
除非开发某些特定产品(如框架),否则我应该关心我的代码,而不是其他(虚拟)代码。其他人可能觉得重用我的代码很有用的事实是一个很好的奖励,而不是我的主要目标。如果他们决定这样做,他们有责任确保扩展的有效性。这适用于整个团队。前期设计对生产力至关重要。
回到我的想法:你的对象应该主要服务于你的目的,而不是它们的子类型的一些可能的shoulda / woulda / coulda功能。你的目标是解决给定的问题。面向对象的语言使用了许多问题(或者更可能是它们的子问题)相似的事实,因此可以使用现有代码来加速进一步的开发。
密封课程会迫使那些可能利用现有代码而无需实际修改您的产品的人重新发明轮子。 (这是我论文的一个重要概念:继承一个类不会修改它!这似乎非常行人且显而易见,但它通常被忽略了。)
人们常常害怕他们的“开放”课程会被扭曲成无法替代其优势的东西。所以呢?你为什么要关心?没有工具可以防止坏程序员创建坏软件!
我并不是试图将可继承类表示为最终正确的设计方法,而是将其视为对我对可继承类的倾向的解释。这就是编程之美 - 几乎无限的正确解决方案,每个都有自己的缺点和优点。欢迎您提出意见和建议。
最后,我对原始问题的回答是:我最终确定了一个类,让其他人知道我认为该类是分层类树的一个叶子,我认为它绝对不可能成为父节点。 (如果有人认为它确实可以,那么要么我错了,要么他们没有得到我)。