案例:造车
现在要造一批悍马汽车,悍马汽车有两个系列:H1和H2。
不考虑任何设计模式,类图如下:
代码:
public abstract class HummerModel {
public abstract void start();
public abstract void stop();
public abstract void alarm();
public abstract void engineBoom();
public abstract void run();
}
public class HummerH1Model extends HummerModel {
@Override
public void start() {
System.out.println("悍马H1发动...");
}
@Override
public void stop() {
System.out.println("悍马H1停车...");
}
@Override
public void alarm() {
System.out.println("悍马H1鸣笛...");
}
@Override
public void engineBoom() {
System.out.println("悍马H1引擎轰鸣...");
}
@Override
public void run() {
start();
engineBoom();
alarm();
stop();
}
}
public class HummerH2Model extends HummerModel {
@Override
public void start() {
System.out.println("悍马H2发动...");
}
@Override
public void stop() {
System.out.println("悍马H2停车...");
}
@Override
public void alarm() {
System.out.println("悍马H2鸣笛...");
}
@Override
public void engineBoom() {
System.out.println("悍马H2引擎轰鸣...");
}
@Override
public void run() {
start();
engineBoom();
alarm();
stop();
}
}
代码第一个优化点:run()方法应该在抽象中就实现。修改一下类图和代码:
public abstract class HummerModel {
public abstract void start();
public abstract void stop();
public abstract void alarm();
public abstract void engineBoom();
public void run() {
start();
engineBoom();
alarm();
stop();
}
}
public class HummerH1Model extends HummerModel {
@Override
public void start() {
System.out.println("悍马H1发动...");
}
@Override
public void stop() {
System.out.println("悍马H1停车...");
}
@Override
public void alarm() {
System.out.println("悍马H1鸣笛...");
}
@Override
public void engineBoom() {
System.out.println("悍马H1引擎轰鸣...");
}
}
public class HummerH2Model extends HummerModel {
@Override
public void start() {
System.out.println("悍马H2发动...");
}
@Override
public void stop() {
System.out.println("悍马H2停车...");
}
@Override
public void alarm() {
System.out.println("悍马H2鸣笛...");
}
@Override
public void engineBoom() {
System.out.println("悍马H2引擎轰鸣...");
}
}
public class Client {
public static void main(String[] args) {
HummerModel h1 = new HummerH1Model();
h1.run();
HummerModel h2 = new HummerH2Model();
h2.run();
}
}
继续优化代码:
- 悍马汽车抽象类的4个抽象方法应该只能对其子类可见,这是因为在业务上,只能由悍马系列(H1和H2)的汽车实现悍马汽车的抽象类,所以将抽象类中的4个抽象方法修改为protected;
- 由于run()方法在子类中是不需要修改的,所以加上final修饰符。
public abstract class Hummer {
protected abstract void start(); // 启动汽车
protected abstract void stop(); // 刹车
protected abstract void alarm(); // 鸣笛
protected abstract void engineBoom(); // 启动引擎
// 汽车行驶的过程
public final void run() {
start();
engineBoom();
alarm();
stop();
}
}
- 在run()方法中,定义了其他抽象方法的调用顺序,并且子类不能重写run()方法的实现逻辑,这样的方法就是模板方法
- start()、stop()、alarm()、engineBoom()这四个方法是子类必须实现的,而且不同的子类有不同的实现,这些方法称为基本方法
- 基本方法分为3种
- 在抽象类中实现的基本方法称为具体方法
- 在子类中实现的基本方法称为抽象方法,本文案例中的这4个方法都是抽象方法
- 钩子方法
补充一下案例,学习钩子方法。
悍马抽象类中添加isAlarm()方法,根据此方法判断汽车是否可以鸣笛,在我们的案例中,悍马H1系列可以鸣笛,H2系列不能鸣笛。
类图:
代码:
public abstract class Hummer {
protected abstract void start(); // 启动汽车
protected abstract void stop(); // 刹车
protected abstract void alarm(); // 鸣笛
protected abstract void engineBoom(); // 启动引擎
// 钩子方法,默认可以鸣笛
protected boolean isAlarm() {
return true;
}
// 汽车行驶的过程
public final void run() {
start();
engineBoom();
if(isAlarm()) {
alarm();
}
stop();
}
}
public class HummerH2 extends Hummer {
@Override
public void start() {
System.out.println("悍马H2启动...");
}
@Override
public void stop() {
System.out.println("悍马H2停止...");
}
@Override
public void alarm() {
System.out.println("悍马H2鸣笛...");
}
@Override
public void engineBoom() {
System.out.println("悍马H2引擎轰鸣...");
}
// H2系列不能鸣笛
@Override
public boolean isAlarm() {
return false;
}
}
public class HummerH1 extends Hummer {
// 是否可以鸣笛
private boolean alarmFlag = true;
// H1系列,用户自己控制是否可以鸣笛
public void setAlarm(boolean isAlarm){
this.alarmFlag = isAlarm;
}
@Override
public void start() {
System.out.println("悍马H1启动...");
}
@Override
public void stop() {
System.out.println("悍马H1停止...");
}
@Override
public void alarm() {
System.out.println("悍马H1鸣笛...");
}
@Override
public void engineBoom() {
System.out.println("悍马H1引擎轰鸣...");
}
// 钩子方法,默认可以鸣笛
@Override
protected boolean isAlarm(){
return this.alarmFlag;
}
}
public class Client {
public static void main(String[] args) {
HummerH1 h1 = new HummerH1();
// 设置H1系列不能鸣笛
h1.setAlarm(false);
h1.run();
HummerH2 h2 = new HummerH2();
h2.run();
}
}
模板方法模式总结:在模板方法中控制各业务逻辑方法的调用顺序,可以使用钩子方法来修改模板方法中的业务逻辑执行逻辑。
通用类图如下:
- templateMethod():模板方法
- primitiveOperation1()、primitiveOperation2():基本方法
- 模板方法是通过对基本方法进行汇总或者排序而产生的结果集
本文原书:《您的设计模式》 作者:CBF4LIFE