基本定义
所谓模板方法模式就是在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤
简单来说就是,抽象类提供一组算法和部分逻辑的实现,具体子类实现剩余逻辑
模式结构
- AbstractClass: 抽象类
- 定义一个或者多个抽象操作,以便让子类实现
- 定义并实现一个模板方法。这个模板方法一般是一个具体方法,它给出了一个顶级逻辑的骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类实现。顶级逻辑也有可能有一些具体方法
- 抽象类剋有任意多个模板方法,而不限于一个。每个模板方法都可以调用任意多个具体方法,
一般模板方法都用final修饰
,子类不允许修改整套逻辑
- ConcreteClass: 具体子类
- 实现父类模板方法中调用的,交由子类实现的抽象方法,父类一般用
abstract
修改这些抽象方法
- 实现父类模板方法中调用的,交由子类实现的抽象方法,父类一般用
这里讲一下基本方法:
-
抽象方法:一个抽象方法由抽象类声明,由具体子类实现,方法使用abstract关键字修饰
-
具体方法:一个具体方法由抽象类声明并实现。一个具体方法没有abstract关键字
-
钩子方法:一个钩子方法由抽象类声明并实现,而子类会加以扩展。通常抽象类给出的实现是一个空的实现,作为方法的默认实现
-
当
抽象类有多个模板方法的时候
public abstract class Parent{ final templateMethod1(){ method1(); method2(); } final templateMethod2(){ } abstract void method1(); abstract void method2(); abstract void method3(); }
假设子类A 关注模板1-> method1, 子类B关注模板2 -> method2, method3
假设不提供空实现,子类A不止要实现自己的method1, 还得实现method2,method3, 当Parent方法一多或者子类很多,那每个类是不是很乱(包含自己不需要实现的方法)
public class ChildrenOne extends Parent { @Override void method1() { } // 不需要的method2, 3 @Override void method2() { } @Override void method3() { } }
-
父类提供空实现
public abstract class Parent { final void templateMethod1(){ method1(); method2(); } final void templateMethod2(){ method3(); } void method1(){} void method2(){}; void method3(){}; } public class ChildrenOne extends Parent { @Override void method1() { } }
-
模板实现
模拟读取不同文件格式,然后输出读取内容的场景,每个处理包括严重文件、解析文件数据、展示数据内容的通用步骤
-
定义抽象类 AbstractHandler
public abstract class AbstractHandler { public final void process(String filepath) { begin(); validate(filepath); parseData(filepath); display(filepath); end(); } abstract void validate(String filepath); abstract void parseData(String filepath); abstract void display(String filepath); // 钩子方法 public void hookMethod() { } public void concreteMethod() { } public void begin() { System.out.println("begin"); } public void end() { System.out.println("end"); } }
-
然后定义子类去实现抽象方法即可:csv、pdf、excel
源码使用
-
InputStream: 复用
// read函数是一个模板方法,定义了读取数据的整个流程,并暴露了一个可以由于子类来定制的抽象方法 public int read(byte b[], int off, int len) throws IOException { if (b == null) { throw new NullPointerException(); } else if (off < 0 || len < 0 || len > b.length - off) { throw new IndexOutOfBoundsException(); } else if (len == 0) { return 0; } int c = read(); if (c == -1) { return -1; } b[off] = (byte)c; int i = 1; try { for (; i < len ; i++) { c = read(); if (c == -1) { break; } b[off + i] = (byte)c; } } catch (IOException ee) { } return i; } // 抽象方法交给子类实现 public abstract int read() throws IOException; public class ByteArrayInputStream extends InputStream { public synchronized int read() { return (pos < count) ? (buf[pos++] & 0xff) : -1; } }
-
Java AbstractList: 复用
// allAll(),add(E e)函数其实可以看作是模板方法,add()是子类需要重写的方法,尽管是没有声明abstract,但是函数直接异常,子类不重写是不能使用的 public boolean addAll(int index, Collection<? extends E> c) { rangeCheckForAdd(index); boolean modified = false; for (E e : c) { add(index++, e); modified = true; } return modified; } public boolean add(E e) { add(size(), e); return true; } public void add(int index, E element) { throw new UnsupportedOperationException(); }
-
Java Servlet: 扩展, 让我们客户端自己去实现抽象方法,而不是框架自带
-
经常的我们使用,定义一个类然后继承HttpServlet, 其实doGet和doPost就是类似子类实现抽象方法
public class HelloServlet extends HttpServlet{ doGet(){ //...... } doPost(){ // ..... } }
-
来看一下HttpServlet的service, Servlet都是走service方法
// service其实就是一个模板方法,实现了整个HTTP的请求流程,DoGet和doPost是模板红可以由子类来定制的部分。实际上,这就相当于Servlet提供一个扩展点(DoGet和doPost).让框架用户在不用修改Servlet框架源码的情况下,将业务代码通过扩展点镶嵌到框架中执行 public void service(ServletRequest req, ServletREsponse res){ HttpServletRequest request= (HttpServletRequest)req String method = request.getMethod if(method.equals(METHOD_GET)){ doGet(); } if(method.equals(METHOD_POST)){ doPost(); } //............. }
-