有什么从具体类继承的好例子吗?

发布于 2024-12-05 13:09:11 字数 1436 浏览 0 评论 0 原文

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

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

发布评论

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

评论(18

勿忘初心 2024-12-12 13:09:11

您通常有一个接口I骨架实现。如果您可以在没有抽象方法的情况下提供可扩展性(例如通过钩子),那么最好有一个非抽象骨架类,因为您可以实例化它。

一个例子是转发包装类,能够转发到实现I的具体类C的另一个对象,例如启用装饰C的简单代码重用,而无需从继承C。您可以在 Effective Java 第 16 项中找到这样的示例,优先选择组合而不是继承。 (由于版权原因,我不想将其发布在这里,但它实际上只是将 I 的所有方法调用转发到包装的实现)。

You often have a skeletal implementations for an interface I. If you can offer extensibility without abstract methods (e.g. via hooks), it is preferable to have a non-abstract skeletal class because you can instantiate it.

An example would be a forwarding wrapper classes, to be able to forward to another object of a concrete class C implementing I, e.g. enabling decoration or simple code-reuse of C without having to inherit from C. You can find such an example in Effective Java item 16, favor composition over inheritance. (I do not want to post it here because of copyrights, but it is really simply forwarding all method calls of I to the wrapped implementation).

双手揣兜 2024-12-12 13:09:11

我认为以下是一个合适的例子:

public class LinkedHashMap<K,V>
    extends HashMap<K,V>

另一个很好的例子是异常的继承:

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

I think the following is a good example when it can be appropriate:

public class LinkedHashMap<K,V>
    extends HashMap<K,V>

Another good example is inheritance of exceptions:

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
只为守护你 2024-12-12 13:09:11

我能想到的一种非常常见的情况是从基本的 UI 控件派生,例如表单、文本框、组合框等。它们是完整的、具体的,并且能够独立存在;然而,它们中的大多数也非常基本,有时它们的默认行为并不是您想要的。例如,几乎没有人会使用纯粹的 Form 实例,除非他们可能正在创建一个完全动态的 UI 层。

例如,在我编写的一个最近达到相对成熟的软件中(这意味着我没有时间主要专注于开发它:)),我发现我需要向 ComboBox 添加“延迟加载”功能,因此它不会第一个窗口加载需要 50 年(以计算机年为单位)。我还需要能够根据另一个组合框中显示的内容自动筛选一个组合框中的可用选项,最后,我需要一种方法将一个组合框的值“镜像”到另一个可编辑控件中,并使一个控件中的更改发生在另一个可编辑控件中。其他也一样。因此,我扩展了基本的 ComboBox 以赋予它这些额外的功能,并创建了两种新类型:LazyComboBox,然后是 MirroringComboBox。两者都基于完全可用的具体 ComboBox 控件,只是覆盖一些行为并添加其他一些行为。它们不是非常松散耦合,因此不太坚固,但添加的功能足够通用,如果必须的话,我可以从头开始重写这些类中的任何一个来完成相同的工作,甚至可能更好。

One very common case I can think of is to derive from basic UI controls, such as forms, textboxes, comboboxes, etc. They are complete, concrete, and well able to stand on their own; however, most of them are also very basic, and sometimes their default behavior isn't what you want. Virtually nobody, for instance, would use an instance of an unadulterated Form, unless possibly they were creating an entirely dynamic UI layer.

For example, in a piece of software I wrote that recently reached relative maturity (meaning I ran out of time to focus primarily on developing it :) ), I found I needed to add "lazy loading" capability to ComboBoxes, so it wouldn't take 50 years (in computer years) for the first window to load. I also needed the ability to automatically filter the available options in one ComboBox based on what was shown in another, and lastly I needed a way to "mirror" one ComboBox's value in another editable control, and make a change in one control happen to the other as well. So, I extended the basic ComboBox to give it these extra features, and created two new types: LazyComboBox, and then further, MirroringComboBox. Both are based on the totally serviceable, concrete ComboBox control, just overriding some behaviors and adding a couple others. They're not very loosely-coupled and therefore not too SOLID, but the added functionality is generic enough that if I had to, I could rewrite either of these classes from scratch to do the same job, possibly better.

场罚期间 2024-12-12 13:09:11

一般来说,我唯一一次从具体类派生是当它们位于框架中时。派生自 AppletJApplet 是一个简单的例子。

Generally speaking, the only time I derive from concrete classes is when they're in the framework. Deriving from Applet or JApplet being the trivial example.

感情旳空白 2024-12-12 13:09:11

这是我当前正在实施的一个示例。

在 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 的访问令牌,

public class TokenTypedAccessToken extends AccessToken {

    private String tokenType;
    //Getter and setter are here...
}

我就可以返回两者和最终用户都一无所知。 :-)

总结: 如果您想要一个与具体类具有相同功能的自定义类,而不更改具体类的结构,我建议扩展具体类。

This is an example of a current implementation that I'm undertaking.

In OAuth 2 environment, since the documentation is still in draft stage, the specification keeps changing (as of time of writing, we're in version 21).

Thus, I had to extend my concrete AccessToken class to accommodate the different access tokens.

In earlier draft, there was no token_type field set, so the actual access token is as follows:

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
}

Now, with Access tokens that returns token_type, I have

public class TokenTypedAccessToken extends AccessToken {

    private String tokenType;
    //Getter and setter are here...
}

So, I can return both and the end user is none the wiser. :-)

In Summary: If you want a customized class that has the same functionality of your concrete class without changing the structure of the concrete class, I suggest extending the concrete class.

千紇 2024-12-12 13:09:11

我主要有 Java 背景,但我对任何语言的示例都感兴趣。

与许多框架一样,ASP.NET 大量使用继承来在类之间共享行为。例如, HtmlInputPassword 具有以下继承层次结构

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

:可以看到具体类的派生示例。

如果您正在构建一个框架 - 并且您确定想要这样做 - 您可能会发现自己需要一个漂亮的大继承层次结构。

I mainly have a Java background, but I'm interested in examples from any language.

Like many frameworks, ASP.NET makes heavy use of inheritance to share behaviour between classes. For example, HtmlInputPassword has this inheritance hierarchy:

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

in which can be seen examples of concrete classes being derived from.

If you're building a framework - and you're sure you want to do that - you may well finding yourself wanting a nice big inheritance hierarchy.

似梦非梦 2024-12-12 13:09:11

其他用例是覆盖默认行为:

假设有一个类使用标准 Jaxb 解析器进行解析

public class Util{

    public void mainOperaiton(){..}
    protected MyDataStructure parse(){
        //standard Jaxb code 
    }
} 

现在假设我想使用一些不同的绑定(比如 XMLBean)进行解析操作,

public class MyUtil extends Util{

    protected MyDataStructure parse(){
      //XmlBean code code 
    }
}

现在我可以使用新的绑定与超类的代码重用。

Other use case would be the to override the default behavior:

Lets say there is a class which uses standard Jaxb parser for parsing

public class Util{

    public void mainOperaiton(){..}
    protected MyDataStructure parse(){
        //standard Jaxb code 
    }
} 

Now say I want to use some different binding (Say XMLBean) for the parsing operation,

public class MyUtil extends Util{

    protected MyDataStructure parse(){
      //XmlBean code code 
    }
}

Now I can use the new binding with code reuse of super class.

独享拥抱 2024-12-12 13:09:11

装饰器模式是一种向类添加额外行为而不使其过于通用的便捷方法,使得大量使用具体类的继承。这里已经提到过,但在某种程度上是“转发包装类”的科学名称。

The decorator pattern, a handy way of adding additional behaviour to a class without making it too general, makes heavy use of inheritance of concrete classes. It was mentioned here already, but under somewhat a scientific name of "forwarding wrapper class".

风轻花落早 2024-12-12 13:09:11

有很多答案,但我想我自己加 0.02 美元。

我很少重写 concreate 类,但在某些特定情况下。当框架类被设计为可扩展时,至少已经提到了 1。我想到了另外两个例子:

1)如果我想调整具体类的行为。有时我想更改具体类的工作方式,或者我想知道何时调用某个方法,以便我可以触发某些内容。通常,具体类会定义一个钩子方法,其唯一用途是让子类重写该方法。

示例:我们重写了 MBeanExporter,因为我们需要能够取消注册 JMX bean:

public class MBeanRegistrationSupport {
    // the concrete class has a hook defined
    protected void onRegister(ObjectName objectName) {
    }

我们的类:

public class UnregisterableMBeanExporter extends MBeanExporter {
    @Override
    protected void onUnregister(ObjectName name) {
        // always a good idea
        super.onRegister(name);
        objectMap.remove(name);
    }

这是另一个很好的示例。 LinkedHashMap 被设计为具有其removeEldestEntry 方法已被重写。

private static class LimitedLinkedHashMap<K, V> extends LinkedHashMap<K, V> {
    @Override
    protected boolean removeEldestEntry(Entry<K, V> eldest) {
        return size() > 1000;
    }

2) 如果一个类与具体类有大量重叠,除了对功能进行一些调整之外。

示例:我的 ORMLite 项目处理持久的 Long 对象字段和 long原始领域。两者的定义几乎相同。 LongObjectType 提供了描述数据库如何处理的所有方法字段。

public class LongObjectType {
    // a whole bunch of methods

LongObjectType 并且只调整一个方法来处理基元。

public class LongType extends LongObjectType {
    ...
    @Override
    public boolean isPrimitive() {
        return true;
    }
}

希望这有帮助。

Lot of answers but I though I'd add my own $0.02.

I override concreate classes infrequently but under some specific circumstances. At least 1 has already been mentioned when framework classes are designed to be extended. 2 additional ones come to mind with some examples:

1) If I want to tweak the behavior of a concrete class. Sometimes I want to change how the concrete class works or I want to know when a certain method is called so I can trigger something. Often concrete classes will define a hook method whose sole usage is for subclasses to override the method.

Example: We overrode MBeanExporter because we need to be able to unregister a JMX bean:

public class MBeanRegistrationSupport {
    // the concrete class has a hook defined
    protected void onRegister(ObjectName objectName) {
    }

Our class:

public class UnregisterableMBeanExporter extends MBeanExporter {
    @Override
    protected void onUnregister(ObjectName name) {
        // always a good idea
        super.onRegister(name);
        objectMap.remove(name);
    }

Here's another good example. LinkedHashMap is designed to have its removeEldestEntry method overridden.

private static class LimitedLinkedHashMap<K, V> extends LinkedHashMap<K, V> {
    @Override
    protected boolean removeEldestEntry(Entry<K, V> eldest) {
        return size() > 1000;
    }

2) If a class shares a significant amount of overlap with the concrete class except for some tweaks to functionality.

Example: My ORMLite project handles persisting Long object fields and long primitive fields. Both have almost the identical definition. LongObjectType provides all of the methods that describe how the database deals with long fields.

public class LongObjectType {
    // a whole bunch of methods

while LongType overrides LongObjectType and only tweaks a single method to say that handles primitives.

public class LongType extends LongObjectType {
    ...
    @Override
    public boolean isPrimitive() {
        return true;
    }
}

Hope this helps.

入怼 2024-12-12 13:09:11
  1. 如果您想扩展辅助库功能,则只能选择继承具体类。

  2. 对于现实生活中的使用示例,您可以查看 DataInputStream 的层次结构

  1. Inheriting concrete class is only option if you want to extend side-library functionality.

  2. For example of real life usage you can look at hierarchy of DataInputStream, that implements DataInput interface for FilterInputStream.

魔法少女 2024-12-12 13:09:11

我开始觉得几乎在任何情况下从具体类继承都是合适的。

这就是“几乎”了。尝试编写一个 而不扩展 Applet< /code> 或 JApplet

这是来自 小程序信息的示例。页面

/* <!-- 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);
    }
}

I'm beginning to feel that there is almost no situation where inheriting from a concrete class is appropriate.

This is one 'almost'. Try writing an without extending Applet or JApplet.

Here is an e.g. from the applet info. page.

/* <!-- 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);
    }
}
不弃不离 2024-12-12 13:09:11

另一个很好的例子是数据存储类型。举一个精确的例子:红黑树是一种更具体的二叉树,但是检索数据和其他信息(例如大小)可以进行相同的处理。当然,一个好的库应该已经实现,但有时您必须为您的问题添加特定的数据类型。

我目前正在开发一个为用户计算矩阵的应用程序。用户可以提供影响计算的设置。可以计算的矩阵有多种类型,但有明显的相似性,特别是在可配置性方面:矩阵 A 可以使用矩阵 B 的所有设置,但具有可以使用的附加参数。在这种情况下,我从 ConfigObjectB 继承了 ConfigObjectA,并且效果非常好。

Another good example would be data storage types. To give a precise example: a red-black tree is a more specific binary tree, but retrieving data and other information like size can be handled identical. Of course, a good library should have that already implemented but sometimes you have to add specific data types for your problem.

I am currently developing an application which calculates matrices for the users. The user can provide settings to influence the calculation. There are several types of matrices that can be calculated, but there is a clear similarity, especially in the configurability: matrix A can use all the settings of matrix B but has additional parameters which can be used. In that case, I inherited from the ConfigObjectB for my ConfigObjectA and it works pretty good.

放低过去 2024-12-12 13:09:11

一般来说,从抽象类继承比从具体类继承更好。具体类必须为其数据表示提供定义,并且某些子类将需要不同的表示。由于抽象类不必提供数据表示形式,因此未来的子类可以使用任何表示形式,而不必担心与它们继承的表示形式发生冲突。
即使我也从未发现过需要具体继承的情况。但在某些情况下可能需要具体继承,特别是当您为软件提供向后兼容性时。在这种情况下,您可能已经专门化了一个 A 类,但您希望它是具体的,因为您的旧应用程序可能正在使用它。

In general, it is better to inherit from an abstract class than from a concrete class. A concrete class must provide a definition for its data representation, and some subclasses will need a different representation. Since an abstract class does not have to provide a data representation, future subclasses can use any representation without fear of conflicting with the one that they inherited.
Even i never found a situation where i felt concrete inheritence is neccessary. But there could be some situations for concrete inheritence specially when you are providing backward compatibility to your software. In that case u might have specialized a class A but you want it to be concrete as your older application might be using it.

深者入戏 2024-12-12 13:09:11

由于您所说的原因,您的担忧也在经典原则“优先考虑组合而不是继承”中得到了回应。我不记得上次从具体类继承是什么时候了。任何需要被子类重用的公共代码几乎总是需要为这些类声明抽象接口。按照这个顺序,我尝试更喜欢以下策略:

  1. 组合(无继承)
  2. 接口
  3. 抽象类

从具体类继承确实不是一个好主意。

[编辑] 我将通过说当控制架构时我看不到它的良好用例来限定此声明。当然,当使用需要它的 API 时,该怎么办?但我不明白这些 API 所做的设计选择。调用类应该始终能够根据依赖倒置原则声明和使用抽象。如果子类有其他要使用的接口,您要么必须违反 DIP,要么进行一些丑陋的转换才能获取这些接口。

Your concerns are also echoed in the classic principle "favor composition over inheritance", for the reasons you stated. I can't remember the last time I inherited from a concrete class. Any common code that needs to be reused by child classes almost always needs to declare abstract interfaces for those classes. In this order I try to prefer the following strategies:

  1. Composition (no inheritance)
  2. Interface
  3. Abstract Class

Inheriting from a concrete class really isn't ever a good idea.

[EDIT] I'll qualify this statement by saying I don't see a good use case for it when you have control over the architecture. Of course when using an API that expects it, whaddaya gonna do? But I don't understand the design choices made by those APIs. The calling class should always be able to declare and use an abstraction according to the Dependency Inversion Principle. If a child class has additional interfaces to be consumed you'd either have to violate DIP or do some ugly casting to get at those interfaces.

情独悲 2024-12-12 13:09:11

来自 gdata 项目

com.google.gdata.client.Service 旨在充当基类,可以针对特定类型的 GData 服务进行定制。

Service javadoc:

Service 类表示与 GData 服务的客户端连接。它封装了与 GData 服务器的所有协议级交互,并充当更高级别实体(提要、条目等)的帮助程序类,这些实体调用服务器上的操作并处理其结果。

此类提供访问任何 GData 服务所需的基本级别通用功能。它还被设计为充当基类,可以针对特定类型的 GData 服务进行自定义。 支持的自定义示例包括:

身份验证 - 为需要身份验证并使用 HTTP 基本或摘要身份验证以外的内容的服务实现自定义身份验证机制。

扩展 - 定义与服务关联的提要、条目和其他类型的预期扩展。

格式 - 定义可能由服务和客户端解析器和生成器使用或生成的其他自定义资源表示形式来处理它们。

from the gdata project:

com.google.gdata.client.Service is designed to act as a base class that can be customized for specific types of GData services.

Service javadoc:

The Service class represents a client connection to a GData service. It encapsulates all protocol-level interactions with the GData server and acts as a helper class for higher level entities (feeds, entries, etc) that invoke operations on the server and process their results.

This class provides the base level common functionality required to access any GData service. It is also designed to act as a base class that can be customized for specific types of GData services. Examples of supported customizations include:

Authentication - implementing a custom authentication mechanism for services that require authentication and use something other than HTTP basic or digest authentication.

Extensions - define expected extensions for feed, entry, and other types associated with a the service.

Formats - define additional custom resource representations that might be consumed or produced by the service and client side parsers and generators to handle them.

旧情勿念 2024-12-12 13:09:11

我发现 java 集合类是一个很好的例子。
所以你有一个 AbstractCollection 和像 AbstractList、AbstractSet、AbstractQueue 这样的子项...
我认为这个层次结构设计得很好……为了确保不会发生爆炸,有 Collections 类及其所有内部静态类。

I find the java collection classes as a very good example.
So you have an AbstractCollection with childs like AbstractList, AbstractSet, AbstractQueue...
I think this hierarchy has been well designed.. and just to ensure there's no explosion there's the Collections class with all its inner static classes.

女皇必胜 2024-12-12 13:09:11

例如,您可以在 GUI 库中执行此操作。从单纯的组件继承并委托给面板没有多大意义。直接从Panel 继承可能要容易得多。

You do that for instance in GUI libraries. It makes not much sense to inherit from a mere Component and delegate to a Panel. It is likely much easyer to inherit from the Panel directly.

安穩 2024-12-12 13:09:11

只是一个一般的想法。抽象类缺少一些东西。如果每个派生类中缺少的内容都不同,那就有意义了。但您可能会遇到这样的情况:您不想修改类,而只想添加一些内容。为了避免重复代码,您将继承。如果您需要这两个类,那么它将是从具体类的继承。

所以我的答案是:在所有情况下,你真的只想添加一些东西。也许这种情况并不经常发生。

Just a general thought. Abstract classes are missing something. It makes sense if this, what is missing, is different in each derived class. But you may have a case where you don't want to modify a class but just want to add something. To avoid duplication of code you would inherit. And if you need both classes it would be inheritance from a concrete class.

So my answer would be: In all cases where you really only want to add something. Maybe this just doesn't happen very often.

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