为什么要防止类被子类化?
阻止类继承的原因可能是什么? (例如在cclass上使用密封)
现在我想不出任何东西。
解决方案
回答
如果我们不确定该接口又不想根据当前接口使用任何其他代码,该怎么办? [这是我的头上的事,但是我也会对其他原因感兴趣!]
编辑:
进行一些谷歌搜索可以得到以下结果:
http://codebetter.com/blogs/patricksmacchia/archive/2008/01/05/rambling-on-the-sealed-keyword.aspx
报价单:
密封类优于未密封类的三个原因:
- 版本控制:当一个类最初被密封时,将来可以更改为未密封,而不会破坏兼容性。 ()
- 性能:()如果JIT编译器看到使用密封类型对虚拟方法的调用,则JIT编译器可以通过非虚拟方式调用该方法来产生更有效的代码。
- 安全性和可预测性:类必须保护自己的状态,并且永远不允许其自身损坏。当类未启封时,派生类可以访问和操纵基类的状态,前提是可以访问而不是私有的内部操纵字段的任何数据字段或者方法。
回答
因为编写可替代地扩展的类非常困难,并且要求我们对未来的用户将如何扩展所编写的内容进行准确的预测。
封闭班级迫使他们使用合成,这更加健壮。
回答
这可能不适用于代码,但是.NET框架中的许多类都是有目的地密封的,因此没有人尝试创建子类。
在某些情况下,内部结构很复杂,需要对某些特定的事物进行非常具体的控制,因此设计人员决定没有人可以继承该类,这样就不会有人通过错误的方式使用某些东西而意外破坏功能。
回答
因为由于种种原因,我们总是希望获得对该类的引用,而不是派生类的引用:
一世。代码其他部分中的不变量
ii。安全
等等
另外,由于在向后兼容性方面是一个安全的选择,如果未密封发布,则永远无法关闭该类以进行继承。
也许我们没有足够的时间来测试该类公开的接口,以确保我们可以允许其他人从该类继承。
也许(现在看到)没有任何意义的是拥有一个子类。
回答
或者,当人们尝试继承子类并且无法获得所有细节时,我们也不想获取错误报告,从而削减了支持成本。
Another user may want to re-use your code by sub-classing your class. I don't see a reason to stop this.
@jjnguy
如果他们想使用我的类的功能,则可以通过遏制来实现,并且结果将使它们的脆性更少。
回答
构图似乎经常被忽略;人们常常想跳上继承潮流。他们不应该!替代性很困难。默认为合成;从长远来看,你会感谢我的。
有时,类接口并非意味深长。公共接口不是虚拟的,而当有人可以覆盖已有的功能时,那将是错误的。是的,通常它们不应覆盖公共接口,但是我们可以通过使类不可继承来确保它们不会被覆盖。
回答
我现在想到的示例是.Net中具有深层克隆的自定义包含类。如果我们从它们那里继承,则会失去深度克隆的能力。[在这个示例中,我有点模糊,自从我与IClonable合作以来已经有一段时间了。它周围,并且我们通常不希望在数据持久层放置很多继承。
Inheritance - subclasses - tends to work against the primary technical imperative you have as a programmer, which is to manage complexity.For the sake of controlling complexity, you should maintain a heavy bias against inheritance.
回答
我想从"代码完成"中给我们以下消息:
我同意jjnguy ...我认为封一堂课的理由很少。恰恰相反,我不止一次要扩展一个类,但是不能这样做,因为它是封闭的。
作为一个完美的例子,我最近创建了一个小程序包(Java,不是C#,但原理相同),将功能包装在memcached工具周围。我想要一个接口,以便在测试中可以模拟掉我正在使用的memcached客户端API,并且还可以根据需要切换客户端(memcached主页上列出了2个客户端)。此外,如果需要出现问题,我想有机会完全替换该功能(例如,由于某些原因,memcached服务器宕机了,我们可能会使用本地缓存实现进行热交换)。
我公开了一个与客户端API交互的最小接口,扩展客户端API类然后在新接口中添加一个Implements子句真是太棒了。然后,我在接口中具有的与实际接口匹配的方法将不需要进一步的详细信息,因此无需显式实现它们。但是,该类是密封的,因此我不得不代理对该类的内部引用的代理调用。结果:没有充分的理由就需要更多的工作和更多的代码。
就是说,我认为在某些情况下,我们可能想密封一个类...而我能想到的最好的事情是,我们将直接调用但允许客户端实现的API。例如,在一个可以对游戏进行编程的游戏中……如果课程没有被密封,那么添加功能的玩家可能会利用API来发挥自己的优势。但是,这是一个非常狭窄的情况,我认为只要我们完全控制代码库,就几乎没有理由将类密封。
回答
这是我非常喜欢Ruby编程语言的原因之一……甚至核心类都是开放的,不仅可以扩展,还可以动态地添加和更改功能,以达到一流的水平!它被称为monkeypatching,如果被滥用可能会成为噩梦,但是玩起来真是太有趣了!
回答
I exposed a minimal interface to interact with the client API, and it would have been awesome to extend the client API class and then just add an implements clause with my new interface. The methods that I had in the interface that matched the actual interface would then need no further details and so I wouldn't have to explicitly implement them. However, the class was sealed, so I had to instead proxy calls to an internal reference to this class. The result: more work and a lot more code for no real good reason.
并非在类中很重要的所有内容都容易在代码中断言。可能存在通过继承和覆盖方法容易破坏的语义和关系。一次覆盖一种方法是执行此操作的一种简单方法。我们将一个类/对象设计为单个有意义的实体,然后有人出现,并认为如果一个或者两个方法"更好",则不会有任何危害。这可能是正确的,也可能不是正确的。也许我们可以在私有和非私有之间,虚拟与虚拟之间以及虚拟与虚拟之间正确分离所有方法,但这可能还不够。要求所有类的继承也给原始开发人员带来了巨大的额外负担,使他们无法预见继承类可能将事情搞砸的所有方式。
我不知道一个完美的解决方案。我对防止继承表示同情,但这也是一个问题,因为它阻碍了单元测试。
回答
Performance: (…) if the JIT compiler sees a call to a virtual method using a sealed types, the JIT compiler can produce more efficient code by calling the method non-virtually.(…)
好吧,这是有原因的:代码现在与memcached接口的更改有所隔离。
确实,这是一个很好的理由。因此,对于性能要求严格的课程,"密封的"和朋友是有意义的。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
回答
到目前为止,我所看到的所有其他原因都归结为"没有人碰我的课!"。如果我们担心有人可能误解了它的内部结构,则在记录它方面做得不好。我们可能无法知道没有什么要添加到类中的内容,或者我们已经知道它的每个可以想象的用例。即使我们是对的,并且其他开发人员不应该使用课程来解决他们的问题,使用关键字也不是防止此类错误的好方法。文档是。如果他们忽略了文档,则会蒙受损失。
继承的唯一合法用途是定义基类的特殊情况,例如,当从Shape继承以派生Circle时。为了检查这一点,可以从相反的方向看这个关系:Shape是圆形的泛化吗?如果答案是肯定的,那么可以使用继承。
因此,如果我们有一个没有特殊案例的类,则应该对其进行密封。
同样由于LSP(Liskov替换原理),人们可以在期望使用基类的情况下使用派生类,这实际上对继承的使用产生了最大的影响:使用基类的代码可以被赋予继承类,并且它仍必须按预期工作。为了在没有明显需要子类的情况下保护外部代码,我们可以密封该类,并且其客户可以依靠其行为不会改变。否则,需要明确设计外部代码,以期望子类中行为的可能更改。
回答
一个更具体的例子是Singleton模式。我们需要密封单例,以确保不会破坏"单例性"。
从面向对象的角度来看,封闭类可以清楚地记录作者的意图,而无需评论。当我密封一个班级时,我想说的是该班级旨在封装一些特定的知识或者某些特定的服务。它无意被增强或者进一步细分。
这与"模板方法"设计模式非常吻合。我有一个界面,上面写着"我正在执行此服务"。然后,我有一个实现该接口的类。但是,如果执行该服务依赖于基类不知道(也不应该知道)的上下文呢?发生的情况是基类提供了受保护或者私有的虚方法,而这些虚方法是子类的钩子,以提供基类不知道和不知道的信息或者操作。同时,基类可以包含所有子类都通用的代码。这些子类将被密封,因为它们旨在完成该服务的唯一实现之一。
我们能否提出这些子类应该进一步子类化以增强它们的论点?我会说不,因为如果该子类不能首先完成工作,那么它永远不会派生自基类。如果我们不喜欢它,那么我们有原始接口,请编写我们自己的实现类。
段落数量不匹配