目录
在看okhttp3源码的时候看到了模板模式,这里顺便记录一下,为了节约时间,总结部分来自网络
一、实例
下面是RealCall.java的部分源码:
final class RealCall implements Call {
//...部分省略
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
final class AsyncCall extends NamedRunnable {
//...部分省略
@Override protected void execute() {
boolean signalledCallback = false;
try {
Response response = getResponseWithInterceptorChain();
if (retryAndFollowUpInterceptor.isCanceled()) {
signalledCallback = true;
responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
} else {
signalledCallback = true;
responseCallback.onResponse(RealCall.this, response);
}
} catch (IOException e) {
if (signalledCallback) {
// Do not signal the callback twice!
Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
} else {
responseCallback.onFailure(RealCall.this, e);
}
} finally {
client.dispatcher().finished(this);
}
}
}
//...
}
@Override public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
最后一句使用到了AsyncCall,AsyncCall继承自NamedRunnable,NamedRunnable实现了Runbale接口,是个抽象类,同时它定义了一个抽象方法execute(),该方法要求子类必须实现,然后在run()方法中被调用
这里采用模板方法的作用是:
1)采用模板方法的设计模式,让子类在 execute()方法具体实现
下面是NamedRunnable的源码:
public abstract class NamedRunnable implements Runnable {
protected final String name;
public NamedRunnable(String format, Object... args) {
this.name = Util.format(format, args);
}
@Override public final void run() {
String oldName = Thread.currentThread().getName();
Thread.currentThread().setName(name);
try {
execute();
} finally {
Thread.currentThread().setName(oldName);
}
}
protected abstract void execute();
}
除了AsyncCall继承了NamedRunnable,ReaderRunnable也继承了它,ReaderRunnable是Http2Connection的内部类
class ReaderRunnable extends NamedRunnable implements Http2Reader.Handler {
//...
@Override protected void execute() {
ErrorCode connectionErrorCode = ErrorCode.INTERNAL_ERROR;
ErrorCode streamErrorCode = ErrorCode.INTERNAL_ERROR;
try {
reader.readConnectionPreface(this);
while (reader.nextFrame(false, this)) {
}
connectionErrorCode = ErrorCode.NO_ERROR;
streamErrorCode = ErrorCode.CANCEL;
} catch (IOException e) {
connectionErrorCode = ErrorCode.PROTOCOL_ERROR;
streamErrorCode = ErrorCode.PROTOCOL_ERROR;
} finally {
try {
close(connectionErrorCode, streamErrorCode);
} catch (IOException ignored) {
}
Util.closeQuietly(reader);
}
}
//...
}
二、总结
为什么要使用模本方法设计模式呢?这个问题是需要考虑到的。
下面一些定义什么的是网上操的
模板方法的定义
定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。它是一种行为型设计模式。
优点:
1)它封装了不变部分,扩展可变部分。它把认为是不变部分的算法封装到父类中实现,而把可变部分算法由子类继承实现,便于子类继续扩展。
2)它在父类中提取了公共的部分代码,便于代码复用。
3)部分方法是由子类实现的,因此子类可以通过扩展方式增加相应的功能,符合开闭原则。
缺点:
1)对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象。
2)父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了代码阅读的难度。
模板方法模式的结构
模板方法模式需要注意抽象类与具体子类之间的协作。它用到了虚函数的多态性技术以及“不用调用我,让我来调用你”的反向控制技术。
模板方法模式的主要角色包括:抽象类(Abstract Class),具体子类(Concrete Class)
(1) 抽象类(Abstract Class)
负责给出一个算法的轮廓和骨架。它由一个模板方法和若干个基本方法构成。这些方法的定义如下。
① 模板方法:定义了算法的骨架,按某种顺序调用其包含的基本方法。
② 基本方法:是整个算法中的一个步骤,包含以下几种类型。
- 抽象方法:在抽象类中申明,由具体子类实现。
- 具体方法:在抽象类中已经实现,在具体子类中可以继承或重写它。
- 钩子方法:在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法两种。
(2) 具体子类(Concrete Class)
实现抽象类中所定义的抽象方法和钩子方法,它们是一个顶级逻辑的一个组成步骤。
(3) 模板方法模式的结构图: