要么改变世界,要么适应世界

设计模式之模板方法

2020-05-14 08:06:00
345
目录

先来看一个简单的例子:假如学院通知开学,那么每个学生所经历的流程就是:收到通知 --> 购票 --> 坐火车/坐飞机/坐大巴/…… --> 返校报道。对应的实现代码如下:

package com.study.design.templateMethod;


public class Main {
    public static void main(String[] args) {
        StudenA A = new StudenA();
        A.process();
        StudenB B = new StudenB();
        B.process();
    }
}

class StudenA {
    public void process() {
        receiveNotification();
        buyTicket();
        byTrain();
        register();
    }

    private void byTrain() {
        System.out.println("====我在火车上啦====");
    }

    private void buyTicket() {
        System.out.println("====我购票啦====");
    }

    private void receiveNotification() {
        System.out.println("====我收到通知啦====");
    }

    private void register() {
        System.out.println("====我注册完毕啦====");
    }
}

class StudenB {
    public void process() {
        receiveNotification();
        buyTicket();
        byPlane();
        register();
    }

    private void byPlane() {
        System.out.println("====我在飞机上啦====");
    }

    private void buyTicket() {
        System.out.println("====我购票啦====");
    }

    private void receiveNotification() {
        System.out.println("====我收到通知啦====");
    }

    private void register() {
        System.out.println("====我注册完毕啦====");
    }
}

观察上面的例子,发现每一个学生有相同之处,也有不同之处,如果每一个学生类都是这么写,就会产生大量冗余代码,而且假如每个学生返校后还要参加开学典礼,那么还要为每一个学生一个方法,在学生类非常多的情况下工作是非常的繁琐。因此我们可以引入模板方法

模板方法

定义:定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。它是一种类行为型模式。

适用场景

  • 算法的整体步骤大体上固定,也存在不同之处,可以将不同的逻辑业务抽象出来,子类进行实现。例如上文中的“收到通知”、“购票”、“返校报道”

  • 子类有公共的行为,可以提出到父类中,避免代码重复。

  • 需要通过子类判断父类中某个步骤是否执行,从而实现子类对父类的反向控制。

代码实现

定义抽象模板类

abstract class BaseProcess {
    public void process() {
        receiveNotification();
        buyTicket();
        backSchool();
        register();
    }

    abstract void backSchool();

    private void buyTicket() {
        System.out.println("====我购票啦====");
    }

    private void receiveNotification() {
        System.out.println("====我收到通知啦====");
    }

    private void register() {
        System.out.println("====我注册完毕啦====");
    }
}

定义实现类

class StudentA extends BaseProcess {

    @Override
    void backSchool() {
        System.out.println("===我通过坐飞机返校的===");
    }
}

class StudentB extends BaseProcess {

    @Override
    void backSchool() {
        System.out.println("===我通过坐火车返校的===");
    }
}

测试类

public class Main {
    public static void main(String[] args) {
        BaseProcess A = new StudentA();
        A.process();
        BaseProcess B = new StudentB();
        B.process();
    }
}

模式扩展

在模板方法模式中,基本方法包含:抽象方法、具体方法和钩子方法,正确使用“钩子方法”可以使得子类控制父类的行为。 例如上文中,不同学生返校的时候可能还有具体不同的行为,例如单身的学生是直接进入校门然后回到宿舍的,但是有男/女朋友的学生可能进入校门后第一件事就是去见另一半。要实现该逻辑只需要在抽象父类中定义抽象钩子方法,子类实现:

abstract class BaseProcess {
    public void process() {
        receiveNotification();
        buyTicket();
        backSchool();
        if (!isSingle()) {
            SeeCouples();
        }
        register();
    }

    abstract void backSchool();

    abstract boolean isSingle();

    private void buyTicket() {
        System.out.println("====我购票啦====");
    }

    private void receiveNotification() {
        System.out.println("====我收到通知啦====");
    }

    private void SeeCouples() {
        System.out.println("好久不见,我快要想死你了~~");
    }

    private void register() {
        System.out.println("====我注册完毕啦====");
    }
}
class StudentB extends BaseProcess {

    @Override
    void backSchool() {
        System.out.println("===我通过坐火车返校的===");
    }

    @Override
    boolean isSingle() {
        return false;
    }
}

主要缺点

  • 需要为每一个基本方法的不同实现提供一个子类 ,假如父类中可变的基本方法很多,那么势必造成实现的子类非常多,后期维护难度增大。
  • 父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果 ,不仅增加了阅读难度,也 违反了里氏替换原则,会给程序带来风险。
历史评论
开始评论