从具体类inheritance的任何好例子?
背景:
作为一名Java程序员,我从接口广泛地inheritance(而不是实现),有时我devise抽象基类。 然而,我从来没有真的觉得有必要对一个具体的(非抽象的)类进行子类化(在我这样做的情况下,后来certificate另一个解决scheme,比如代表团会更好)。
所以现在我开始觉得,从具体的课程中inheritance是不合适的。 一方面, Liskov替代原则似乎几乎不可能满足非平凡的阶级; 在这里还有许多其他的问题似乎回应了类似的意见。
所以我的问题是:
在哪种情况下(如果有)从具体类inheritance是否有意义? 你能否给出一个具体的,真实的例子,从另一个具体的类inheritance的类,你觉得这是最好的devise给定的约束? 我会特别感兴趣的例子,满足LSP(或例如满足LSP似乎不重要)。
我主要有一个Java背景,但我对任何语言的例子感兴趣。
你经常有一个接口I
的骨架实现 。 如果你可以在没有抽象方法的情况下提供可扩展性(比如通过钩子),那么最好有一个非抽象的骨架类,因为你可以实例化它。
一个例子是转发包装类 ,能够转发到实现I
的具体类C
另一个对象,例如,启用C
装饰或简单的代码重用,而不必从C
inheritance。 您可以在Effective Java项目16中find这样一个例子,支持合成而不是inheritance。 (我不想在这里发布它是因为版权,但它实际上只是将I
所有方法调用转发给包装的实现)。
我认为以下是适当的一个很好的例子:
public class LinkedHashMap<K,V> extends HashMap<K,V>
另一个很好的例子是例外的inheritance:
public class IllegalFormatPrecisionException extends IllegalFormatException public class IllegalFormatException extends IllegalArgumentException public class IllegalArgumentException extends RuntimeException public class RuntimeException extends Exception public class Exception extends Throwable
我能想到的一个很常见的情况就是从基本的UI控件中派生出来,比如窗体,文本框,combobox等等。它们是完整的,具体的,并且能够自立的。 然而,他们中的大多数也是非常基本的,有时他们的默认行为不是你想要的。 实际上,没有人会使用一个无掺杂的表单的实例,除非他们可能创build了一个完全dynamic的UI层。
例如,我写的一个软件最近达到了相对成熟的程度(意思是我没有时间专注于开发它:)),我发现我需要为ComboBox添加“延迟加载”function,所以它不会“第一个窗口加载需要50年(电脑年)。 我还需要能够根据另一个ComboBox中显示的内容自动过滤可用选项,最后我需要一种方法来在另一个可编辑控件中“镜像”一个ComboBox的值,并对发生在其他也是如此。 所以,我扩展了基本的ComboBox以赋予这些额外的function,并创build了两个新的types:LazyComboBox,然后是MirroringComboBox。 两者都基于完全可用,具体的combobox控件,只是覆盖一些行为,并添加一些其他人。 它们不是非常松散的耦合,因此不是太固定,但是增加的function足够通用,如果必须的话,我可以重写这两个类中的任何一个来完成相同的工作,可能更好。
一般来说,我唯一从具体类派生出来的就是他们在框架中的时候。 从Applet
或JApplet
派生是一个微不足道的例子。
这是我正在执行的当前实现的示例。
在OAuth 2环境中,由于文档仍处于草稿阶段,所以规范不断变化(截至编写时,我们在版本21中)。
因此,我必须扩展具体的AccessToken
类以适应不同的访问令牌。
在之前的草案中,没有设置token_type
字段,所以实际访问令牌如下所示:
public class AccessToken extends OAuthToken { /** * */ private static final long serialVersionUID = -4419729971477912556L; private String accessToken; private String refreshToken; private Map<String, String> additionalParameters; //Getters and setters are here }
现在,使用返回token_type
Access令牌,我有
public class TokenTypedAccessToken extends AccessToken { private String tokenType; //Getter and setter are here... }
所以,我可以返回并且最终用户是没有智慧的。 🙂
总结:如果你想在不改变具体类的结构的情况下创build一个具有相同function的自定义类,我build议扩展具体类。
我主要有一个Java背景,但我对任何语言的例子感兴趣。
像许多框架一样,ASP.NET大量使用inheritance来在类之间共享行为。 例如, HtmlInputPassword有这个inheritance层次结构:
System.Object System.Web.UI.Control System.Web.UI.HtmlControls.HtmlControl // abstract System.Web.UI.HtmlControls.HtmlInputControl // abstract System.Web.UI.HtmlControls.HtmlInputText System.Web.UI.HtmlControls.HtmlInputPassword
在其中可以看到具体类的例子来源于。
如果你正在构build一个框架 – 而且你确定要这样做 – 你可能会发现自己想要一个不错的大inheritance层次结构。
其他的用例是重写默认行为:
可以说有一个类使用标准的Jaxbparsing器进行parsing
public class Util{ public void mainOperaiton(){..} protected MyDataStructure parse(){ //standard Jaxb code } }
现在说我想使用一些不同的绑定(说XMLBean)进行parsing操作,
public class MyUtil extends Util{ protected MyDataStructure parse(){ //XmlBean code code } }
现在我可以使用新的绑定与超级类的代码重用。
装饰器模式是一种向类中添加附加行为的方便方法,而不会使其过于笼统,因此大量使用具体类的inheritance。 这里已经提到过,但是在某种程度上是“转发包装类”的科学名称。
很多的答案,但我虽然我会增加我自己的0.02美元。
在某些特定情况下,我很less重写concreate类。 当框架类被devise为扩展时,至less已经提到了1个。 有两个额外的例子,
1)如果我想调整一个具体类的行为。 有时我想改变具体类的工作方式,或者我想知道什么时候调用某个方法,这样我就可以触发一些东西。 通常具体的类将定义一个钩子方法,其唯一用法是用于子类来覆盖该方法。
例如:我们覆盖了MBeanExporter
因为我们需要能够注销一个JMX bean:
public class MBeanRegistrationSupport { // the concrete class has a hook defined protected void onRegister(ObjectName objectName) { }
我们class:
public class UnregisterableMBeanExporter extends MBeanExporter { @Override protected void onUnregister(ObjectName name) { // always a good idea super.onRegister(name); objectMap.remove(name); }
这是另一个很好的例子。 LinkedHashMap
被devise为重载removeEldestEntry
方法。
private static class LimitedLinkedHashMap<K, V> extends LinkedHashMap<K, V> { @Override protected boolean removeEldestEntry(Entry<K, V> eldest) { return size() > 1000; }
2)如果一个类与具体类共享大量的重叠,除了一些function的调整。
例如:我的ORMLite项目处理持久的Long
对象字段和long
原始字段。 两者几乎都有相同的定义。 LongObjectType
提供了描述数据库如何处理long
字段的所有方法。
public class LongObjectType { // a whole bunch of methods
而LongType
重写LongObjectType
,只调整一个方法来说处理原语。
public class LongType extends LongObjectType { ... @Override public boolean isPrimitive() { return true; } }
希望这可以帮助。
-
如果要扩展侧面库function,inheritance具体类只是选项。
-
例如现实生活中的用法,您可以看看DataInputStream的层次结构,它实现了FilterInputStream的DataInput接口。
我开始觉得从具体的课程inheritance是几乎没有的情况是适当的。
这是一个“几乎”。 尝试编写一个小程序而不扩展Applet
或JApplet
。
这是一个例如从applet信息。 页面 。
/* <!-- Defines the applet element used by the appletviewer. --> <applet code='HelloWorld' width='200' height='100'></applet> */ import javax.swing.*; /** An 'Hello World' Swing based applet. To compile and launch: prompt> javac HelloWorld.java prompt> appletviewer HelloWorld.java */ public class HelloWorld extends JApplet { public void init() { // Swing operations need to be performed on the EDT. // The Runnable/invokeLater() ensures that happens. Runnable r = new Runnable() { public void run() { // the crux of this simple applet getContentPane().add( new JLabel("Hello World!") ); } }; SwingUtilities.invokeLater(r); } }
另一个很好的例子是数据存储types。 举一个精确的例子:红黑树是一个更具体的二叉树,但是检索数据和其他大小的信息可以处理相同。 当然,一个好的库应该已经实现了,但是有时候你必须为你的问题添加特定的数据types。
我目前正在开发一个为用户计算matrix的应用程序。 用户可以提供设置来影响计算。 有几种types的matrix可以计算出来,但是有一个明显的相似性,特别是在可configuration性方面:matrixA可以使用matrixB的所有设置,但是可以使用附加的参数。 在这种情况下,我从ConfigObjectBinheritanceConfigObjectA,它工作的很好。
一般来说,从一个抽象类inheritance比从一个具体类inheritance更好。 具体类必须为其数据表示提供一个定义,而某些子类将需要不同的表示。 由于抽象类不必提供数据表示,所以未来的子类可以使用任何表示,而不用担心与他们inheritance的冲突。 即使我从来没有发现一种情况,我觉得具体的inheritance是必要的。 但是,当您向软件提供向后兼容性时,可能会出现一些具体的inheritance问题。 在这种情况下,你可能有一个专门的class A
但你希望它是具体的,因为你的旧应用程序可能正在使用它。
你所关心的问题也在经典的原则“ 赞成构成比inheritance ”中得到回应。 我不记得我最后一次从一个具体的类inheritance。 任何需要由子类重用的通用代码几乎总是需要为这些类声明抽象接口。 按照这个顺序,我尝试使用以下策略:
- 构成(无inheritance)
- 接口
- 抽象类
从一个具体的类inheritance真的不是一个好主意。
[编辑]我将通过说,当你有控制体系结构时看不到一个好的用例。 当然,使用期望的API时,whaddaya会怎么做? 但我不明白这些API所做的deviseselect。 调用类应该总是能够根据依赖倒置原则来声明和使用抽象。 如果一个子类有额外的接口要消耗,你要么必须违反DIP或做一些丑陋的铸造来获得这些接口。
从gdata项目 :
com.google.gdata.client.Service旨在充当可以为特定types的GData服务定制的基类。
服务javadoc:
Service类表示一个到GData服务的客户端连接。 它封装了与GData服务器的所有协议级别的交互,并充当调用服务器上的操作并处理其结果的更高级实体(提要,条目等)的助手类。
该类提供访问任何GData服务所需的基本级别的通用function。 它也被devise为可以为特定types的GData服务定制的基类。 支持的自定义示例包括:
身份validation – 为需要身份validation的服务实现自定义身份validation机制,并使用HTTP基本身份validation或摘要身份验
扩展 – 定义与服务相关联的订阅源,条目和其他types的预期扩展。
格式 – 定义服务和客户端parsing器和生成器可能使用或生成的其他自定义资源表示来处理它们。
我发现Java集合类是一个很好的例子。 所以你有AbstractList,AbstractSet,AbstractQueue等孩子的AbstractCollection …我认为这个层次结构已经很好的devise了,只是为了确保没有爆炸的Collections类的所有内部静态类。
例如在GUI库中就是这样做的。 从单纯的组件和委派到一个面板inheritance是没有多大意义的。 直接从面板inheritance的可能性更大。
只是一个普遍的想法。 抽象类缺less一些东西。 如果这个缺失的东西在每个派生类中是不同的,那么这是有道理的。 但是,你可能有一个情况,你不想修改一个类,但只是想添加一些东西。 为了避免重复的代码,你会inheritance。 如果你需要这两个类,它将从一个具体的类inheritance。
所以我的答案是:在所有的情况下,你只是想添加一些东西。 也许这不会经常发生。