模板方法模式——java设计模式(二十四)

简介

模板方法模式(Template Method Pattern):定义一个操作中算法的框架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构即可重定义算法的某些特定步骤。

  • 将一系列复杂流程封装在基本方法中,并在模板方法中安排这些基本方法的逻辑,子类可以重写基本方法完成功能的定制。
  • 模板方法:将基本方法组合在一起形成的总方法,给出了一个顶层的逻辑框架。
  • 基本方法:实现各个步骤的方法,分为3种。
    • 抽象方法:由抽象类声明,子类实现。
    • 具体方法:在抽象类中声明和实现,子类可以覆盖也可以继承。
    • 钩子方法:在抽象类中声明和实现,子类可能加以扩展,从而控制父类的行为。
  • 钩子方法分为两种。
    • 和具体步骤挂钩,返回boolean,控制父类方法的执行。
    • 实现体为空方法,子类可以通过覆盖添加行为,也可继承默认不执行。

结构和实现

  • 角色包括:
    • 抽象类:定义一系列基本方法,并通过模板方法整合。
    • 具体子类:实现抽象类的抽象方法,根据需要覆盖具体方法、钩子方法。
  • 模板方法结构。
    180515.template.png

实例

  • 开发利息计算模块,流程如下:验证用户、根据账号类型计算利息、显示利息。
    180515.interest.png

钩子方法使用

  • 钩子方法可以使子类控制父类的行为,或者增加父类的行为。
  • 开发数据图表显示模块,流程:获取数据、将数据转换为XML、以某种图表的形式显示XML数据。其中数据源可能为XML格式,也可能是其他格式,不一定需要转换。
    180515.view.png
  • 显示数据的方式可以用桥接模式优化。

优缺点和适用范围

  • 优点:
    • 修改细节整体步骤不变。子类中只会修改细节实现,或者控制流程,整个逻辑不变。
    • 增加子类方便。继承抽象类即可,不同子类提供基本方法的不同实现,符合开闭原则和单一职责原则。
    • 复用代码。类库中的公共行为放入抽象类,共子类复用。
    • 可反向控制。通过钩子函数控制某些步骤的执行。
  • 缺点:
    • 类增加较多。为每一个基本方法的实现提供一个子类,如果可变方法太多,类的数量也多,系统复杂,可以结合桥接模式优化。
  • 适用范围:
    • 需要分割复杂算法,实现不可变部分,将可变部分放在子类中实现。
    • 将子类的公共方法提取到公共父类中,避免代码重复。
    • 通过钩子方法实现子类对父类的反向控制。

jdk中的应用

  • ThreadPoolExecutor中的内部类Worker调用外部类中的beforeExecute和afterExecute这两个空方法,这两个空方法可以被覆盖。Worker和ThreadPoolExecutor是抽象类,子类是具体子类。
private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable {
    final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            while (task != null || (task = getTask()) != null) {
                w.lock();
                // If pool is stopping, ensure thread is interrupted;
                // if not, ensure thread is not interrupted.  This
                // requires a recheck in second case to deal with
                // shutdownNow race while clearing interrupt
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&
                      runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                    wt.interrupt();
                try {
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
                        task.run();
                    } catch (RuntimeException x) {
                        thrown = x; throw x;
                    } catch (Error x) {
                        thrown = x; throw x;
                    } catch (Throwable x) {
                        thrown = x; throw new Error(x);
                    } finally {
                        afterExecute(task, thrown);
                    }
                } finally {
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);
        }
    }
}

猜你喜欢

转载自blog.csdn.net/qq_40369829/article/details/80370639