代码复用神器,模板模式实操分享

开发 前端
模板模式,顾名思义,定义一个模板,将部分逻辑以具体方法或者具体构造函数的形式实现,在抽象类中声明一些抽象方法来迫使子类实现剩余的逻辑。

 

[[336153]]

本文转载自微信公众号「 Java极客技术」,作者 鸭血粉丝。转载本文请联系 Java极客技术公众号。

一、介绍

模板模式,顾名思义,定义一个模板,将部分逻辑以具体方法或者具体构造函数的形式实现,在抽象类中声明一些抽象方法来迫使子类实现剩余的逻辑。

不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现,这就是模板方法模式的用意。

模板模式涉及到三个角色:

  • 抽象类(AbstractClass):实现了模板方法,定义了算法的骨架;
  • 具体类(ConcreteClass):实现抽象类中的抽象方法,已完成完整的算法;
  • 客户角色:客户类提出使用具体类的请求;

二、示例

举个例子,以早上起床到上班所需要的操作为例,大致流程可以分为以下几步:穿衣服、刷牙、洗脸、吃早餐等。男生和女生的操作可能有些区别。

我们创建一个抽象的类,定义好大致的操作流程,如下:

  1. /** 
  2.  * 抽象类 
  3.  */ 
  4. public abstract class AbstractPerson { 
  5.  
  6.     /** 
  7.      * 定义操作流程 
  8.      */ 
  9.     public void prepareGoWorking(){ 
  10.         dressing();//穿衣服 
  11.         brushTeeth();//刷牙 
  12.         washFace();//洗脸 
  13.         eatBreakFast();//吃早餐 
  14.     } 
  15.  
  16.     /**穿衣服*/ 
  17.     protected abstract void dressing(); 
  18.  
  19.     /**刷牙*/ 
  20.     protected void brushTeeth(){ 
  21.         System.out.println("刷牙"); 
  22.     } 
  23.  
  24.     /**洗脸*/ 
  25.     protected void washFace(){ 
  26.         System.out.println("洗脸"); 
  27.     } 
  28.  
  29.     /**吃早餐*/ 
  30.     protected abstract void eatBreakFast(); 
  31.  

因为男生和女生的行为不一样,我们分别创建两个具体类,如下:

  1. /** 
  2.  * 男生 
  3.  * 具体实现类 
  4.  */ 
  5. public class ManPerson extends AbstractPerson{ 
  6.  
  7.     @Override 
  8.     protected void dressing() { 
  9.         System.out.println("穿西装"); 
  10.     } 
  11.  
  12.     @Override 
  13.     protected void eatBreakFast() { 
  14.         System.out.println("直接在公司吃早餐"); 
  15.     } 
  1. /** 
  2.  * 女生 
  3.  * 具体实现类 
  4.  */ 
  5. public class WomanPerson extends AbstractPerson{ 
  6.  
  7.     @Override 
  8.     protected void dressing() { 
  9.         System.out.println("穿休闲衣服"); 
  10.     } 
  11.  
  12.     @Override 
  13.     protected void eatBreakFast() { 
  14.         System.out.println("在家弄点吃的,或者在外面买一点小吃"); 
  15.     } 

创建一个客户端,实现如下:

  1. public class TemplateClient { 
  2.  
  3.     public static void main(String[] args) { 
  4.         //男生起床步骤 
  5.         ManPerson manPerson = new ManPerson(); 
  6.         System.out.println("-----男生起床步骤----"); 
  7.         manPerson.prepareGoWorking(); 
  8.         System.out.println("-----女生起床步骤----"); 
  9.         //女生起床步骤 
  10.         WomanPerson womanPerson = new WomanPerson(); 
  11.         womanPerson.prepareGoWorking(); 
  12.     } 

输出结果:

  1. -----男生起床步骤---- 
  2. 穿西装 
  3. 刷牙 
  4. 洗脸 
  5. 直接在公司吃早餐 
  6. -----女生起床步骤---- 
  7. 穿休闲衣服 
  8. 刷牙 
  9. 洗脸 
  10. 在家弄点吃的,或者在外面买一点小吃 

当然,模版模式的玩法,还不仅仅只有这些,还可以在模版模式中使用挂钩(hook)。

什么是hook呢?存在一个空实现的方法,我们称这种方法为hook。子类可以视情况来决定是否要覆盖它。

还是以上面为例子,比如吃完早餐就要出门上班,选择什么交通工具呢?

抽象类新增方法hook(),内容如下:

  1. /** 
  2.  * 抽象类 
  3.  */ 
  4. public abstract class AbstractPerson { 
  5.  
  6.     /** 
  7.      * 定义操作流程 
  8.      */ 
  9.     public void prepareGoWorking(){ 
  10.         dressing();//穿衣服 
  11.         brushTeeth();//刷牙 
  12.         washFace();//洗脸 
  13.         eatBreakFast();//吃早餐 
  14.         hook();//挂钩 
  15.     } 
  16.  
  17.     /**穿衣服*/ 
  18.     protected abstract void dressing(); 
  19.  
  20.     /**刷牙*/ 
  21.     protected void brushTeeth(){ 
  22.         System.out.println("刷牙"); 
  23.     } 
  24.  
  25.     /**洗脸*/ 
  26.     protected void washFace(){ 
  27.         System.out.println("洗脸"); 
  28.     } 
  29.  
  30.     /**吃早餐*/ 
  31.     protected abstract void eatBreakFast(); 
  32.  
  33.     /**挂钩*/ 
  34.     protected void hook(){}; 
  35.  

男生具体实现类,重写hook()方法,内容如下:

  1. /** 
  2.  * 男生 
  3.  * 具体实现类 
  4.  */ 
  5. public class ManPerson extends AbstractPerson{ 
  6.  
  7.     @Override 
  8.     protected void dressing() { 
  9.         System.out.println("穿西装"); 
  10.     } 
  11.  
  12.     @Override 
  13.     protected void eatBreakFast() { 
  14.         System.out.println("直接在公司吃早餐"); 
  15.     } 
  16.  
  17.     @Override 
  18.     protected void hook() { 
  19.         System.out.println("乘地铁上班"); 
  20.     } 

运行测试类,男生具体实现类,输出结果:

  1. -----男生起床步骤---- 
  2. 穿西装 
  3. 刷牙 
  4. 洗脸 
  5. 直接在公司吃早餐 
  6. 乘地铁上班 

当然,还有其他的玩法,比如女生洗完脸之后,可能需要化妆,我们再次将抽象类进行处理,内容如下:

  1. /** 
  2.  * 抽象类 
  3.  */ 
  4. public abstract class AbstractPerson { 
  5.  
  6.     /** 
  7.      * 定义操作流程 
  8.      */ 
  9.     public void prepareGoWorking(){ 
  10.         dressing();//穿衣服 
  11.         brushTeeth();//刷牙 
  12.         washFace();//洗脸 
  13.         //是否需要化妆,默认不化妆 
  14.         if(isMakeUp()){ 
  15.             System.out.println("进行化妆"); 
  16.         } 
  17.         eatBreakFast();//吃早餐 
  18.         hook();//挂钩 
  19.     } 
  20.  
  21.     /**是否需要化妆方法*/ 
  22.     protected boolean isMakeUp(){ 
  23.         return false
  24.     } 
  25.  
  26.     /**穿衣服*/ 
  27.     protected abstract void dressing(); 
  28.  
  29.     /**刷牙*/ 
  30.     protected void brushTeeth(){ 
  31.         System.out.println("刷牙"); 
  32.     } 
  33.  
  34.     /**洗脸*/ 
  35.     protected void washFace(){ 
  36.         System.out.println("洗脸"); 
  37.     } 
  38.  
  39.     /**吃早餐*/ 
  40.     protected abstract void eatBreakFast(); 
  41.  
  42.     /**挂钩*/ 
  43.     protected void hook(){}; 
  44.  

女生具体实现类,重写isMakeUp()方法,内容如下:

  1. /** 
  2.  * 女生 
  3.  * 具体实现类 
  4.  */ 
  5. public class WomanPerson extends AbstractPerson{ 
  6.  
  7.     @Override 
  8.     protected void dressing() { 
  9.         System.out.println("穿休闲衣服"); 
  10.     } 
  11.  
  12.     @Override 
  13.     protected void eatBreakFast() { 
  14.         System.out.println("在家弄点吃的,或者在外面买一点小吃"); 
  15.     } 
  16.  
  17.     @Override 
  18.     protected boolean isMakeUp() { 
  19.         return true
  20.     } 

运行测试类,女生具体实现类,输出结果:

  1. -----女生起床步骤---- 
  2. 穿休闲衣服 
  3. 刷牙 
  4. 洗脸 
  5. 进行化妆 
  6. 在家弄点吃的,或者在外面买一点小吃 

三、应用

模版设计模式,应用非常广泛,比如javaEE中的servlet,当我们每创建一个servlet的时候,都会继承HttpServlet,其实HttpServlet已经为我们提供一套操作流程,我们只需要重写里面的方法即可!

 

HttpServlet 的部分源码如下:

  1. public abstract class HttpServlet extends GenericServlet { 
  2.     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 
  3.         // ... 
  4.     } 
  5.     protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 
  6.         // ... 
  7.     } 
  8.     protected void doHead(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 
  9.         // ... 
  10.     } 
  11.     protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 
  12.         // ... 
  13.     } 
  14.     protected void doDelete(HttpServletRequest req,  HttpServletResponse resp) throws ServletException, IOException { 
  15.         // ... 
  16.     } 
  17.     protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 
  18.         // ... 
  19.     } 
  20.     protected void doTrace(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 
  21.         // ... 
  22.     } 
  23.      
  24.     protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 
  25.         String method = req.getMethod(); 
  26.  
  27.         if (method.equals(METHOD_GET)) { 
  28.             long lastModified = getLastModified(req); 
  29.             if (lastModified == -1) { 
  30.                 // servlet doesn't support if-modified-since, no reason 
  31.                 // to go through further expensive logic 
  32.                 doGet(req, resp); 
  33.             } else { 
  34.                 long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE); 
  35.                 if (ifModifiedSince < lastModified) { 
  36.                     // If the servlet mod time is later, call doGet() 
  37.                     // Round down to the nearest second for a proper compare 
  38.                     // A ifModifiedSince of -1 will always be less 
  39.                     maybeSetLastModified(resp, lastModified); 
  40.                     doGet(req, resp); 
  41.                 } else { 
  42.                     resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED); 
  43.                 } 
  44.             } 
  45.  
  46.         } else if (method.equals(METHOD_HEAD)) { 
  47.             long lastModified = getLastModified(req); 
  48.             maybeSetLastModified(resp, lastModified); 
  49.             doHead(req, resp); 
  50.  
  51.         } else if (method.equals(METHOD_POST)) { 
  52.             doPost(req, resp); 
  53.              
  54.         } else if (method.equals(METHOD_PUT)) { 
  55.             doPut(req, resp); 
  56.              
  57.         } else if (method.equals(METHOD_DELETE)) { 
  58.             doDelete(req, resp); 
  59.              
  60.         } else if (method.equals(METHOD_OPTIONS)) { 
  61.             doOptions(req,resp); 
  62.              
  63.         } else if (method.equals(METHOD_TRACE)) { 
  64.             doTrace(req,resp); 
  65.              
  66.         } else { 
  67.             // 
  68.             // Note that this means NO servlet supports whatever 
  69.             // method was requested, anywhere on this server. 
  70.             // 
  71.  
  72.             String errMsg = lStrings.getString("http.method_not_implemented"); 
  73.             Object[] errArgs = new Object[1]; 
  74.             errArgs[0] = method; 
  75.             errMsg = MessageFormat.format(errMsg, errArgs); 
  76.              
  77.             resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg); 
  78.         } 
  79.     } 
  80.      
  81.     // ...省略... 

自定义一个 HelloWorld 的 Servlet 类,如下:

  1. import java.io.*; 
  2. import javax.servlet.*; 
  3. import javax.servlet.http.*; 
  4.  
  5. public class HelloWorld extends HttpServlet { 
  6.  
  7.   public void init() throws ServletException { 
  8.     // ... 
  9.   } 
  10.  
  11.   public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 
  12.       response.setContentType("text/html"); 
  13.       PrintWriter out = response.getWriter(); 
  14.       out.println("<h1>Hello World!</h1>"); 
  15.   } 
  16.    
  17.   public void destroy() { 
  18.       // ... 
  19.   } 

四、总结

模版模式有着许多的优点:

1、模板方法模式通过把不变的行为搬移到超类,去除了子类中的重复代码;2、子类实现算法的某些细节,有助于算法的扩展;3、通过一个父类调用子类实现的操作,通过子类扩展增加新的行为,符合开放-封闭原则;

也有些缺点:1、每个不同的实现都需要定义一个子类,这会导致类的个数的增加,设计更加抽象

 

如果某些类有一些共同的行为,可以使用模版设计模式,创建一个抽象类,将共同的行为定义在抽象类中,可以有效的减少子类重复的代码。

 

责任编辑:武晓燕 来源: Java极客技术
相关推荐

2021-07-09 07:21:40

SpringBootRedisLUA

2010-04-12 09:36:29

Oacle merge

2010-01-06 10:38:16

Linux安装JDK

2022-07-04 07:37:51

模板模式重构

2022-12-15 17:50:14

2010-04-27 10:25:28

Oracle Subs

2010-05-18 12:24:16

MySQL binlo

2010-05-20 15:53:15

配置MySQL

2009-11-30 13:05:00

Suse防火墙

2010-05-18 18:19:40

MySQL修改表结构

2009-12-01 18:03:56

Linux版本

2010-04-15 14:18:30

Oracle创建

2010-05-10 17:00:53

Oracle死锁进程

2010-04-09 10:13:13

Oracle数据字典

2010-04-13 14:00:00

Oracle inse

2021-09-22 15:36:31

勒索软件攻击数据泄露

2024-04-28 10:52:25

CentOS系统RHEL系统

2018-10-15 10:15:30

STM32Linux经验

2010-05-14 17:56:16

SQL优化索引

2010-06-13 17:29:10

MySQL连接数
点赞
收藏

51CTO技术栈公众号