设计模式之模板方法模式

84 阅读3分钟

案例:造车

现在要造一批悍马汽车,悍马汽车有两个系列: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