繁体   English   中英

默认实现还是抽象方法?

[英]Default implementation or abstract method?

将方法的默认实现放在超类中更好,并在子类想要偏离它时重写它,或者你应该只留下超类方法摘要,并在许多子类中重复正常实现?

例如,我参与的项目有一个类,用于指定它应该停止的条件。 抽象类如下:

public abstract class HaltingCondition{
    public abstract boolean isFinished(State s);
}

一个简单的实现可能是:

public class AlwaysHaltingCondition extends HaltingCondition{
    public boolean isFinished(State s){
        return true;
    }
}

我们用对象做这个的原因是我们可以随意组合这些对象。 例如:

public class ConjunctionHaltingCondition extends HaltingCondition{
    private Set<HaltingCondition> conditions;

    public void isFinished(State s){
        boolean finished = true;
        Iterator<HaltingCondition> it = conditions.iterator();
        while(it.hasNext()){
            finished = finished && it.next().isFinished(s);
        }
        return finished;
    }
}

但是,我们有一些暂停条件需要通知事件已经发生。 例如:

public class HaltAfterAnyEventHaltingCondition extends HaltingCondition{
    private boolean eventHasOccurred = false;

    public void eventHasOccurred(Event e){
        eventHasOccurred = true;
    }

    public boolean isFinished(State s){
        return eventHasOccurred;
    }
}

我们应该如何在抽象超类中最好地表示eventHasOccurred(Event e) 大多数子类可以具有此方法的无操作实现(例如, AlwaysHaltingCondition ),而有些子类需要重要的实现才能正常运行(例如HaltAfterAnyEventHaltingCondition ),而其他子类不需要对消息执行任何操作,但必须将其传递给其下属这样它们才能正常运行(例如ConjunctionHaltingCondition )。

我们可以有一个默认实现,它可以减少代码重复,但会导致一些子类编译但如果没有被覆盖则不能正常运行,或者我们可以将方法声明为abstract,这将需要每个子类的作者考虑一下它们提供的实现,尽管十分之九是无操作实现。 这些策略的其他优缺点是什么? 一个比另一个好多少?

一种选择是有另一个抽象类,作为超对于这确实想使用的默认实现全部实现使用。

就个人而言,我通常将抽象类中的非最终方法抽象(或者仅使用接口),但它肯定取决于具体情况。 例如,如果你有一个包含许多方法的接口,并且你希望能够选择加入其中一些方法,那么一个抽象类就可以为每个方法以无操作的方式实现接口。

基本上,你需要根据其优点来评估每个案例。

听起来你担心在事件发生时设置该布尔变量。 如果用户重写eventHasOccurred() ,则不会设置布尔变量,并且isFinished()将不返回正确的值。 为此,您可以使用一个用户覆盖的抽象方法来处理事件,另一个方法调用抽象方法并设置布尔值(请参阅下面的代码示例)。

此外,不是将eventHasOccurred()方法放在HaltingCondition类中,您可以让需要处理事件的类扩展一个定义此方法的类(如下面的类)。 任何不需要处理事件的类都可以扩展HaltingCondition

public abstract class EventHaltingCondition extends HaltingCondition{
  private boolean eventHasOccurred = false;

  //child class implements this
  //notice how it has protected access to ensure that the public eventHasOccurred() method is called
  protected abstract void handleEvent(Event e);

  //program calls this when the event happens
  public final void eventHasOccurred(Event e){
    eventHasOccurred = true; //boolean is set so that isFinished() returns the proper value
    handleEvent(e); //child class' custom code is executed
  }

  @Override
  public boolean isFinished(){
    return eventHasOcccurred;
  }
}

编辑(见评论):

final EventHaltingCondition condition = new EventHaltingCondition(){
  @Override
  protected void handleEvent(Event e){
    //...
  }
};
JButton button = new JButton("click me");
button.addActionListener(new ActionListener(){
  public void actionPerformed(ActionEvent actionEvent){
    //runs when the button is clicked

    Event event = //...
    condition.eventHasOccurred(event);
  }
});

如果要将任何实现放在抽象基类中,它应该是使用no-op实现的子类的代码,因为这是一个对基类也有意义的实现。 如果基类没有合理的实现(例如,如果你在这里讨论的方法没有明智的无操作),那么我建议将它留作抽象。

对于重复的代码,如果存在所有使用相同方法实现的类的“族”,并且您不希望在该族中的所有类中复制代码,则可以简单地使用每个族的帮助程序类。这些实现。 在您的示例中,传递事件的类的助手,类的助手接受并记录事件等。

当我创建与工作中的其他人一起开发的应用程序的基本大纲(类层次结构)时,我遇到了类似的情况。 我选择放置方法摘要 (并因此强制其实现)是出于通信目的。

基本上,其他队友已经以某种方式明确地实现了该方法,因此首先注意到它的存在,并且第二个同意他们返回那里,即使它只是默认实现。

基类中的默认实现经常被忽略。

如果您要离开超类方法摘要,您可能需要考虑使用接口(不要与接口混淆)。 因为接口是不提供具体实现的接口。

斯科特

为了扩展,当我们作为程序员被指示经常对新的,有时经验丰富的接口进行编码时,开发人员会错误地认为它是在引用关键字接口,其中没有找到实现细节。 然而,更明确的说法是任何顶级对象都可以被视为可以与之交互的界面。 例如,名为Animal的抽象类将是一个名为Cat的类,它将继承自Animal。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM