我想知道是否在设计供其他人使用的框架时会不赞成,一类具有某些功能作为默认行为,并期望其客户在必要时覆盖它。一个示例如下所示:
public class RecordProcessor<T extends Record> {
// ...
public void process() {
// process record logic
}
}
该库的用户创建其具体类以处理自己的T
类型的记录。
现在,我想添加一个名为preProcess()
的函数,以使使用者能够预处理其记录。然后看起来像这样:
public class RecordProcessor<T extends Record> {
// ...
public void process() {
preprocess();
// process record logic
}
public void preProcess() {
// By default no preprocessing
}
}
我知道我可以使preProcess
成为抽象函数,但是由于两个原因,我不想这样做:
并非所有客户都需要预处理他们的记录
我们有一个自动部署推送代码的管道结构,因此将RecordProcessor
设为抽象类将立即破坏客户的应用程序。
让preProcess
在父类中什么也不做,而让子类覆盖它是不好的做法吗?如果不是,最好的方法是让客户知道他们现在可以预处理记录了吗?通过Java文档?
答案 0 :(得分:3)
一种方法是将公共方法标记为最终方法(但这也可能会破坏现有应用程序),并允许覆盖受保护的钩子方法。例如:
public class RecordProcessor<T extends Record> {
// ...
public final void process() {
doPreProcess();
doProcess();
doPostProcess();
}
protected void doPreProcess() {
// By default no preprocessing
return;
}
protected void doProcess() {
// some default implementation
}
protected void doPostProcess() {
// By default no postprocessing
return;
}
}
拥有一些文档应该使其他开发人员很自然地认识到可选的扩展方法。
使用什么也不做的钩子方法,我没有发现任何问题。但是,它应该包含return语句,以便静态分析工具不会抱怨。
更新:为了避免破坏现有应用程序,请尽可能将现有方法标记为不推荐使用,并引入一种新方法。例如:
public class RecordProcessor<T extends Record> {
// ...
public final void execute() {
doPreProcess();
doProcess();
doPostProcess();
}
@Deprecated - use execute() method instead.
public void process() {
doProcess();
}
protected void doPreProcess() {
// By default no preprocessing
return;
}
protected void doProcess() {
// some default implementation
}
protected void doPostProcess() {
// By default no postprocessing
return;
}
}
答案 1 :(得分:2)
优先考虑组成而不是继承。如果希望客户添加自定义预处理,则可以通过委派给单独的对象来进行。
public interface RecordPreProcessor<T extends Record>{
public void process(T record);
}
public class RecordProcessor<T extends Record> {
private RecordPreProcessor<T> recordPreProcessor = null;
public void setRecordPreProcessor(RecordPreProcessor<T> recordPreProcessor) {
this.recordPreProcessor = recordPreProcessor;
}
public void process() {
if (recordPreProcessor != null) recordPreProcessor.process(record);
// process record logic
}
}
答案 2 :(得分:0)
否,在Java中不建议重写。
您的方法是一种允许子类扩展其父类行为的合理方法。还有其他选择,例如将行为作为对象传递。但是,没有一种真正的方法。
可以改善代码的一种方法是将preProcess()标记为受保护的。这是该类的实现细节。您不希望任何人拥有RecordProcessor来决定他们可以自己调用preProcess(),对吗?
public class RecordProcessor<T extends Record> {
...
protected void preProcess() {
^^^^^^^^^
// By default no preprocessing
}
}
另一种改进此方法的方法是考虑是否打算让任何人创建超类RecordProcessor的实例。如果您不这样做,请使该类为抽象,以防止出现这种情况。如果您愿意,可以使用类名来表示,或者您的编码指南也可以要求。
public abstract class AbstractRecordProcessor<T extends Record> {
^^^^^^^^ ^^^^^^^^
...
protected void preProcess() {
// By default no preprocessing
}
}
记录此类方法的一种常用方法是使用短语“默认实现不执行任何操作。子类可以覆盖此方法...” 。例如,下面是java.util.concurrent.FutureTask.done()
的文档。您可以通过在线搜索该词组的第一句话来找到更多示例。
public class FutureTask<V> implements RunnableFuture<V> {
...
/**
* Protected method invoked when this task transitions to state
* {@code isDone} (whether normally or via cancellation). The
* default implementation does nothing. Subclasses may override
* this method to invoke completion callbacks or perform
* bookkeeping. Note that you can query status inside the
* implementation of this method to determine whether this task
* has been cancelled.
*/
protected void done() { }
}
答案 3 :(得分:0)
我最终做的是-在@tsolakp的启发下,我认为还不错,它只是创建了RecordProcessor
的子类,称为PreprocessRecordProcessor
之类。这没有办法干扰现有的代码,因为没有涉及到现有的代码。该类将是这样的:
public class PreprocessRecordProcessor<T extends Record> extends RecordProcessor<T> {
// ...
public void process() {
preProcess();
super.process();
}
protected abstract void preProcess();
}
如果该库的客户想要添加自己的逻辑,则可以简单地扩展此类,并且可以强制提供预处理逻辑(假定可以选择提供,如果他们忘记了,可能会导致意外结果。)