默认实现还是抽象方法?
-
02-10-2019 - |
题
将方法的默认实现在超类中放置,并在子类希望偏离此时覆盖它,或者您是否应该将超级类方法摘要摘要,并在许多子类中重复进行正常的实现?
例如,我参与的一个项目的课程用于指定应停止的条件。抽象类如下:
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
).
我们可以进行默认实施,这将减少代码重复,但会导致某些子类编译,但如果没有被覆盖,则无法正确操作,或者我们可以将方法称为摘要,这需要每个子类的作者才能考虑一下他们提供的实施,尽管十分之九,这将是一个无障碍实施。这些策略的其他利弊是什么?一个比另一个好吗?
解决方案
一个选项是拥有另一个抽象子类,用作所有实现的超级类 做 想要使用默认实现。
我个人 通常 在抽象类中放置非最终方法摘要(或者只是使用接口),但这绝对取决于情况。如果您有许多方法的接口,并且您希望能够选择进入 一些 例如,其中的一个抽象类,该类以每种方法的无效方式实现界面是可以的。
基本上,您需要根据其优点评估每个案例。
其他提示
如果您要将任何实现都放在抽象基类中,则应该是使用NO-OP实现的子类的代码,因为这是对基类也有意义的实现。如果基类没有明智的实现(例如,如果您在此处讨论的方法没有明智的否,那么我建议将其抽象。
关于重复的代码,如果有类似于该方法实现的类的“家庭”,并且您不想在家庭中的所有类中重复代码这些实现。在您的示例中,用于传递事件的课程的助手,课程的助手接受并记录事件,等等。
听起来您担心事件发生时设置布尔值变量。如果用户覆盖 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);
}
});
当我创建了与其他工作中的其他应用程序一起开发的应用程序的基本轮廓(类层次结构)时,我也遇到了类似的情况。我选择的方法 抽象的 (因此是为了迫使其实施)是为了 沟通 目的。
基本上,其他队友有某种方式明确实施该方法,因此首先注意到它的存在,第二 同意 关于他们返回那里的内容,即使只是默认实现。
基础类中的默认实现通常会被忽略。
如果要离开超级类方法摘要,则可能需要考虑利用接口(不要与接口混淆)。因为该接口是不提供具体实现的界面。
斯科特
为了扩展,当我们作为程序员被指示将代码到接口的代码通常是新的,有时甚至是经验丰富的,开发人员会错误地假设它是指未找到实现详细信息的关键字界面。但是,更清楚的方法是,任何顶层对象都可以视为可以与之交互的接口。例如,一个称为动物的抽象类是一个界面,将是一个名为CAT的类,它将从动物那里继承。