模板方法模式
银行办理业务
银行办理业务一般要经过以下4个流程:取号、排队、办理具体业务、对银行工作人员进行评分等,其中取号、排队和对银行工作人员进行评分的业务对每个客户是一样的,但是办理具体业务却因人而异,它可能是存款、取款或者转账等。生活中有很多类似的例子,像这样大致固定的流程称为模板,细节不同的可以根据实际需求再做实现。
银行业务流程
实现银行办理业务的四个流程:取号、排队、办理具体业务、对银行工作人员进行评分,其中“办理具体业务“是什么具体业务暂时不知道,使用抽象方法 doSomeThing 来实现。
package com.skystep.设计模式.模板方法;
public abstract class BankService {
private void takeNumber() {
System.out.println("取号...");
}
private void lineUp() {
System.out.println("排队...");
}
protected abstract void doSomeThing();
private void score() {
System.out.println("评分...");
}
public void service() {
takeNumber();
lineUp();
doSomeThing();
score();
}
}
如果要做转账业务,只需要实现继承 BankService 并且在 doSomeThing 方法中做存款的业务逻辑处理。
package com.skystep.设计模式.模板方法;
public class DepositService extends BankService{
@Override
protected void doSomeThing() {
System.out.println("存款...");
}
}
如果要做转账业务,只需要实现继承 BankService 并且在 doSomeThing 方法中做存款的转账逻辑处理。
package com.skystep.设计模式.模板方法;
public class TransferService extends BankService{
@Override
protected void doSomeThing() {
System.out.println("转账...");
}
}
如果要做转账业务,只需要实现继承 BankService 并且在 doSomeThing 方法中做存款的取款逻辑处理。
package com.skystep.设计模式.模板方法;
public class WithdrawalService extends BankService{
@Override
protected void doSomeThing() {
System.out.println("取款...");
}
}
那么写一个客户端类,让银行的不同业务跑起来。
package com.skystep.设计模式.模板方法;
public class Client {
public static void main(String[] args) {
System.out.println("存储业务");
BankService bankService = new DepositService();
bankService.service();
System.out.println("转账业务");
bankService = new TransferService();
bankService.service();
System.out.println("取业务");
bankService = new WithdrawalService();
bankService.service();
}
}
客户端端类调用了三个业务类,办理三种不同的具体业务,但是“取号”、“排队”、“评分”的业务逻辑相同。
存储业务
取号...
排队...
存款...
评分...
转账业务
取号...
排队...
转账...
评分...
取业务
取号...
排队...
取款...
评分...
显然,一般银行的“取号”、“排队”、“评分”的操作是相同,这些处理逻辑被封装到 BankService,其他具体业务类继承 BankService,便继承了这些业务能力。实现了代码复用。这是日常编码中遇到最多的场景。
回归模式
像这样大致固定的流程,将具体细节实现延缓到子类的处理模式称为“模板方法模式”。该模式在我们实际编程大量使用,可以用来解决一些方法通用,却在每一个子类都重新写了这一方法的问题。
“模板方法模式”中存在着两个重要角色:
-
抽象模板:抽象模板类,它给出了一个顶级逻辑的骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类实现,如 BankService 类。
-
具体模板:实现父类所定义的一个或多个抽象方法,在该函数中实现具体的业务逻辑,如 DepositService 类、TransferService类。
应用场景
责任链在实际业务中有很多应用:
- 多个需求,每个需求大部分业务流程基本相同,少部分不同,使用“模板方法模式”,可以复用相同业务的代码;
- 如果需求中出现比较重要的,复杂的方法可以使用“模板方法模式”;减低后期具体业务的实现的难度;
线程设计
先来看看如何实现一个线程并且把它运行起来。
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程:" + Thread.currentThread().getName());
}
});
thread.start();
Thread 实现了 Runnable 接口,在实例化需要实现 run 函数,之所以提供 run 方法,是因为对于调用者来说,每个线程的业务不相同。必须延缓到子类中实现。就像 BankService 中的 doSomeThing ,在客户来之前,银行是不知道每个客户的具体业务。延迟到 DepositService 等类中实现。
来看看线程 start 方法的代码。
/**
* Causes this thread to begin execution; the Java Virtual Machine
* calls the <code>run</code> method of this thread. // 注意看这个注释
* <p>
* The result is that two threads are running concurrently: the
* current thread (which returns from the call to the
* <code>start</code> method) and the other thread (which executes its
* <code>run</code> method).
* <p>
* It is never legal to start a thread more than once.
* In particular, a thread may not be restarted once it has completed
* execution.
*
* @exception IllegalThreadStateException if the thread was already
* started.
* @see #run()
* @see #stop()
*/
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
private native void start0();
/**
* If this thread was constructed using a separate
* <code>Runnable</code> run object, then that
* <code>Runnable</code> object's <code>run</code> method is called;
* otherwise, this method does nothing and returns.
* <p>
* Subclasses of <code>Thread</code> should override this method.
*
* @see #start()
* @see #stop()
* @see #Thread(ThreadGroup, Runnable, String)
*/
@Override
public void run() {
if (target != null) {
target.run();
}
}
注释 calls the run
method of this thread 翻译回来便是:让这个线程开始执行,JVM 会调用这个线程的 run() 方法。不管调用实现什么样的业务,对于 JVM 来讲都需要做相同的准备工作,而这些相同且复杂的处理逻辑被统一封装起来,从而让使用者更加方便使用线程。这是“模板方法模式”的第二种应用场景。