概述

模板方法

模板方法定义了一个算法的步骤,并允许子类为一个或多个步骤提供实现。那么什么是模板方法呢?我们看下模板方法的定义。

  1. 一个具体方法而非抽象方法,其用作一个算法的模板;
  2. 在模板方法中,算法内的大多数步骤都被某个方法代表;
  3. 模板方法中某些方法是子类处理
  4. 需要由子类提供的方法,必须在超类中声明为抽象方法;
  5. 模板方法通常不能被覆盖,也就是使用final修饰;

模板方法模式(Template Method Pattern),又叫模板模式(Template Pattern),指在一个抽象类公开定义了执行它的方法的模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行简单说,模板方法模式定义一个操作中的算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构,就可以重定义该算法的某些特定步骤。

这种类型的设计模式属于行为型模式。

如下实例,prepareRecipe就是一个模板方法。

public abstract class CaffeineBeverage {
   final void  prepareRecipe(){
       boilWater();
       brew();
       pourInCup();
       addCondiments();
   }
   protected abstract void addCondiments();
   protected abstract void pourInCup();
   protected abstract void brew();
   protected abstract void boilWater();
}

模板方法模式

在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。

这个模式是用来创建一个算法的模板。什么是模板?如你所见的,模板就是一个方法。更具体地说,这个方法将算法定义成一组步骤,其中的任何步骤都可以是抽象的,由子类负责实现。这可以确保算法的结构保持不变,同时由子类提供部分实现。

对上面实例进一步扩展,我们看下抽象类内可以有哪些类型的方法。

public abstract class CaffeineBeverage {
   final void  prepareRecipe(){
       boilWater();
       brew();
       pourInCup();
       addCondiments();
       concreteOperation();
       hook();
   }
   protected abstract void addCondiments();
   protected abstract void pourInCup();
   protected abstract void brew();
   protected abstract void boilWater();
      final void concreteOperation(){
	// 这里是实现
   }
	//空方法
   void hook(){};
}

可以看到有一个具体的concreteOperation方法,final表示其不可以被子类覆盖。我们也可以由“默认不做事的方法”,我们称这种方法为“hook”(钩子)。子类可以视情况决定要不要覆盖他们。

钩子是一种被声明在 抽象类中的方法,但只有空的或者默认的实现。钩子的存在,可以让子类有能力对算法的不同点进行挂钩。要不要挂钩,由子类决定。每一个具体的子类都必须定义所有的抽象方法,并为模板方法算法中未定义步骤提供完整的实现。

那么什么时候使用抽象方法什么时候使用钩子呢?

答,当你的子类必须提供算法中某个算法或步骤的实现时,就是用抽象方法。如果算法的这个部分是可选的,就用钩子。如果是钩子的话,子类可以选择实现这个钩子,但并不强制这么做。

使用钩子的真正目的是什么?

钩子有几种用法。如我们之前所说的,钩子可以让子类实现算法中可选的部分,或者在钩子对于子类的实现并不重要的时候,子类可以对此钩子置之不理。钩子的另一个用法,是让子类能够有机会对模板方法中某些即将发生的(或刚刚发生的)步骤作出反应。比方说,名为justReOrderedList()的钩子方法允许子类在内部列表重新组织后执行某些动作(例如在屏幕上重新显示数据)。正如前面提到的,钩子也可以让子类有能力为其抽象类作一些决定。

好莱坞原则

好莱坞原则简单来讲就是:别调用我们,我们会调用你。

好莱坞原则可以给我们一种防止“依赖腐败”的方法。当高层组件依赖低层组件,而低层组件又依赖高层组件,而高层组件又依赖边侧组件,边侧组件又依赖低层组件时,依赖腐败就发生了。在这种情况下,没有人可以轻易地搞懂系统是如何设计的。

在好莱坞原则之下,我们允许低层组件将自己挂钩到系统上,但是高层组件会决定什么时候和怎样使用这些低层组件。换句话说,高层组件对待低层组件的方式就是“别调用我们,我们会调用你”。

模板方法模式就契合该原则。当我们设计模板方法模式时,我们告诉子类,“不要调用我们,我们会调用你”。

那么低层组件完全不可以调用高层组件的方法吗?并不尽然!

事实上,低层组件在结束时,常常调用从超类中继承来的方法。我们所要做的是,避免让高层和低层组件之间有明显的环状依赖。

好莱坞原则与依赖倒置原则

依赖倒置原则教我们尽量避免使用具体类,而多使用抽象类。而好莱坞原则是用在创建框架或组件上的一种技巧,好让低层组件能够被挂钩进计算中,而且又不会让高层组件依赖低层组件。两者的目标都是在于解耦,但是依赖倒置原则更加注重如何在设计中避免依赖。

好莱坞原则教我们一个技巧,创建一个有弹性的设计,允许低层结构能够互相操作,而又防止其他类太过依赖它们。

真实案例

数组类Arrays的mergeSort方法就是一个模板方法。

 private static void mergeSort(Object[] src,
                                  Object[] dest,
                                  int low,
                                  int high,
                                  int off) {
        int length = high - low;
        // Insertion sort on smallest arrays
        if (length < INSERTIONSORT_THRESHOLD) {
            for (int i=low; i<high; i  )
                for (int j=i; j>low &&
                         ((Comparable) dest[j-1]).compareTo(dest[j])>0; j--)
                    swap(dest, j, j-1);
            return;
        }
        // Recursively sort halves of dest into src
        int destLow  = low;
        int destHigh = high;
        low   = off;
        high  = off;
        int mid = (low   high) >>> 1;
        mergeSort(dest, src, low, mid, -off);
        mergeSort(dest, src, mid, high, -off);
        // If list is already sorted, just copy from src to dest.  This is an
        // optimization that results in faster sorts for nearly ordered lists.
        if (((Comparable)src[mid-1]).compareTo(src[mid]) <= 0) {
            System.arraycopy(src, low, dest, destLow, length);
            return;
        }
        // Merge sorted halves (now in src) into dest
        for(int i = destLow, p = low, q = mid; i < destHigh; i  ) {
            if (q >= high || p < mid && ((Comparable)src[p]).compareTo(src[q])<=0)
                dest[i] = src[p  ];
            else
                dest[i] = src[q  ];
        }
    }

这里方法中,swap方法是一个具体方法。每一个元素需要实现compareTo方法,“填补”模板方法的缺憾。

可能会有疑问,上面这个案例真的是一个模板方法吗?

答,其符合模板方法的精神。这个模式的重点在于提供一个算法,并让子类实现某些步骤。而数组的排序做法很明显地并非如此!但是,我们都知道。Java api中的模式并非总是如同教科书例子一般地中规中矩,为了符合当前的环境和实现的约束,它们总是要被适当地修改。这个Arrays类sort方法的设计者受到一些约束。通常我们无法设计一个类继承Java数组,而sort()方法希望能够适用于所有的数组(每个数组都是不同的类)。所以它们定义了一个静态方法,而由被排序的对象内的每个元素自行提供比较大小的算法部分。所以,这虽然不是教科书上的模板方法,但它的实现仍然符合模板方法模式的精神。再者,由于不需要基础数组就可以使用这个方法,这样使得排序变得更有弹性、更有用。

另外一个案例是java.io的InputStream类有一个read()方法,是由子类实现的,而这个方法又会被read(byte b[], int off, int len)模板方法使用。

模板方法模式的注意事项和细节

基本思想是:算法只存在于一个地方,也就是在父类中,容易修改。需要修改算法时,只要修改父类的模板方法或者已经实现的某些步骤,子类就会继承这些修改。

实现了最大化代码复用。父类的模板方法和已实现的某些步骤会被子类继承而直接使用。 既统一了算法,也提供了很大的灵活性。父类的模板方法确保了算法的结构保持不变,同时由子类提供部分步骤的实现。

该模式的不足之处:每一个不同的实现都需要一个子类实现,导致类的个数增加,使得系统更加庞大。一般模板方法都加上final 关键字, 防止子类重写模板方法。

模板方法模式使用场景:当要完成在某个过程,该过程要执行一系列步骤,这一系列的步骤基本相同,但其个别步骤在实现时可能不同,通常考虑用模板方法模式来处理。

到此这篇关于Java设计模式之模板方法模式Template Method Pattern详解的文章就介绍到这了,更多相关Java模板方法模式内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

Java设计模式之模板方法模式Template Method Pattern详解的更多相关文章

  1. 7.3 Swift原始值

    //原始型是整型enumMethod:Int{caseAddcaseSubcaseMulcaseDiv}print("--------------->")//这个还是枚举值print(Method.Mul)//print(Method.Add.toRaw())print("--------------->")print(Method.Mul.rawValue)print(Method.Mul.ha

  2. swift – ‘Method’对于此上下文中的类型查找是不明确的,Alamofire中的错误

    我使用Alamofire的网络处理在swift和运行一个奇怪的错误。看起来我们不能通过Method枚举作为参数。[错误是在方法参数]您必须指定从哪个模块查找对象类型。

  3. 当我使用android.widget.RelativeLayout.setBackground时,NoSuchMethodError

    在android4.2我的应用程序工作正常,但是当我尝试使用android4.0.4测试它有这个消息错误:我使用setBackgroud添加一些动画,像这样的代码我有支持库添加到我的项目,我不知道如何解决这个,任何帮助将被apreciated!

  4. Java利用POI实现导入导出Excel表格

    这篇文章主要为大家详细介绍了Java利用POI实现导入导出Excel表格,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  5. Java 阻塞队列BlockingQueue详解

    本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景,通过实例代码介绍了Java 阻塞队列BlockingQueue的相关知识,需要的朋友可以参考下

  6. Java Bean 作用域及它的几种类型介绍

    这篇文章主要介绍了Java Bean作用域及它的几种类型介绍,Spring框架作为一个管理Bean的IoC容器,那么Bean自然是Spring中的重要资源了,那Bean的作用域又是什么,接下来我们一起进入文章详细学习吧

  7. Java实现世界上最快的排序算法Timsort的示例代码

    Timsort 是一个混合、稳定的排序算法,简单来说就是归并排序和二分插入排序算法的混合体,号称世界上最好的排序算法。本文将详解Timsort算法是定义与实现,需要的可以参考一下

  8. Java日期工具类的封装详解

    在日常的开发中,我们难免会对日期格式化,对日期进行计算,对日期进行校验,为了避免重复写这些琐碎的逻辑,我这里封装了一个日期工具类,方便以后使用,直接复制代码到项目中即可使用,需要的可以参考一下

  9. Java设计模式之模板方法模式Template Method Pattern详解

    在我们实际开发中,如果一个方法极其复杂时,如果我们将所有的逻辑写在一个方法中,那维护起来就很困难,要替换某些步骤时都要重新写,这样代码的扩展性就很差,当遇到这种情况就要考虑今天的主角——模板方法模式

  10. Java 中 Class Path 和 Package的使用详解

    这篇文章主要介绍了Java 中 Class Path和Package的使用详解,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的朋友可以参考一下

随机推荐

  1. 基于EJB技术的商务预订系统的开发

    用EJB结构开发的应用程序是可伸缩的、事务型的、多用户安全的。总的来说,EJB是一个组件事务监控的标准服务器端的组件模型。基于EJB技术的系统结构模型EJB结构是一个服务端组件结构,是一个层次性结构,其结构模型如图1所示。图2:商务预订系统的构架EntityBean是为了现实世界的对象建造的模型,这些对象通常是数据库的一些持久记录。

  2. Java利用POI实现导入导出Excel表格

    这篇文章主要为大家详细介绍了Java利用POI实现导入导出Excel表格,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  3. Mybatis分页插件PageHelper手写实现示例

    这篇文章主要为大家介绍了Mybatis分页插件PageHelper手写实现示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  4. (jsp/html)网页上嵌入播放器(常用播放器代码整理)

    网页上嵌入播放器,只要在HTML上添加以上代码就OK了,下面整理了一些常用的播放器代码,总有一款适合你,感兴趣的朋友可以参考下哈,希望对你有所帮助

  5. Java 阻塞队列BlockingQueue详解

    本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景,通过实例代码介绍了Java 阻塞队列BlockingQueue的相关知识,需要的朋友可以参考下

  6. Java异常Exception详细讲解

    异常就是不正常,比如当我们身体出现了异常我们会根据身体情况选择喝开水、吃药、看病、等 异常处理方法。 java异常处理机制是我们java语言使用异常处理机制为程序提供了错误处理的能力,程序出现的错误,程序可以安全的退出,以保证程序正常的运行等

  7. Java Bean 作用域及它的几种类型介绍

    这篇文章主要介绍了Java Bean作用域及它的几种类型介绍,Spring框架作为一个管理Bean的IoC容器,那么Bean自然是Spring中的重要资源了,那Bean的作用域又是什么,接下来我们一起进入文章详细学习吧

  8. 面试突击之跨域问题的解决方案详解

    跨域问题本质是浏览器的一种保护机制,它的初衷是为了保证用户的安全,防止恶意网站窃取数据。那怎么解决这个问题呢?接下来我们一起来看

  9. Mybatis-Plus接口BaseMapper与Services使用详解

    这篇文章主要为大家介绍了Mybatis-Plus接口BaseMapper与Services使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  10. mybatis-plus雪花算法增强idworker的实现

    今天聊聊在mybatis-plus中引入分布式ID生成框架idworker,进一步增强实现生成分布式唯一ID,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部