会员登录 - 用户注册 - 设为首页 - 加入收藏 - 网站地图 代码复用神器,模板模式实操分享!

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

时间:2025-11-05 13:50:50 来源:益强数据堂 作者:系统运维 阅读:199次

本文转载自微信公众号「 Java极客技术」,代码作者 鸭血粉丝。复用分享转载本文请联系 Java极客技术公众号。神器实操

一、模板模式介绍

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

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

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

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

二、示例

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

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

/**  * 抽象类  */ public abstract class AbstractPerson {     /**      * 定义操作流程      */     public void prepareGoWorking(){         dressing();//穿衣服         brushTeeth();//刷牙         washFace();//洗脸         eatBreakFast();//吃早餐     }     /**穿衣服*/     protected abstract void dressing();     /**刷牙*/     protected void brushTeeth(){         System.out.println("刷牙");     }     /**洗脸*/     protected void washFace(){         System.out.println("洗脸");     }     /**吃早餐*/     protected abstract void eatBreakFast(); } 

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

/**  * 男生  * 具体实现类  */ public class ManPerson extends AbstractPerson{     @Override     protected void dressing() {         System.out.println("穿西装");     }     @Override     protected void eatBreakFast() {         System.out.println("直接在公司吃早餐");     } }  /**  * 女生  * 具体实现类  */ public class WomanPerson extends AbstractPerson{     @Override     protected void dressing() {         System.out.println("穿休闲衣服");     }     @Override     protected void eatBreakFast() {         System.out.println("在家弄点吃的,或者在外面买一点小吃");     } } 

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

public class TemplateClient {     public static void main(String[] args) {         //男生起床步骤         ManPerson manPerson = new ManPerson();         System.out.println("-----男生起床步骤----");         manPerson.prepareGoWorking();         System.out.println("-----女生起床步骤----");         //女生起床步骤         WomanPerson womanPerson = new WomanPerson();         womanPerson.prepareGoWorking();     } } 

输出结果:

-----男生起床步骤---- 穿西装 刷牙 洗脸 直接在公司吃早餐 -----女生起床步骤---- 穿休闲衣服 刷牙 洗脸 在家弄点吃的,或者在外面买一点小吃 

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

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

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

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

/**  * 抽象类  */ public abstract class AbstractPerson {     /**      * 定义操作流程      */     public void prepareGoWorking(){         dressing();//穿衣服         brushTeeth();//刷牙         washFace();//洗脸         eatBreakFast();//吃早餐         hook();//挂钩     }     /**穿衣服*/     protected abstract void dressing();     /**刷牙*/     protected void brushTeeth(){         System.out.println("刷牙");     }     /**洗脸*/     protected void washFace(){         System.out.println("洗脸");     }     /**吃早餐*/     protected abstract void eatBreakFast();     /**挂钩*/     protected void hook(){}; } 

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

/**  * 男生  * 具体实现类  */ public class ManPerson extends AbstractPerson{     @Override     protected void dressing() {         System.out.println("穿西装");     }     @Override     protected void eatBreakFast() {         System.out.println("直接在公司吃早餐");     }     @Override     protected void hook() {         System.out.println("乘地铁上班");     } } 

运行测试类,男生具体实现类,站群服务器输出结果:

-----男生起床步骤---- 穿西装 刷牙 洗脸 直接在公司吃早餐 乘地铁上班 

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

/**  * 抽象类  */ public abstract class AbstractPerson {     /**      * 定义操作流程      */     public void prepareGoWorking(){         dressing();//穿衣服         brushTeeth();//刷牙         washFace();//洗脸         //是否需要化妆,默认不化妆         if(isMakeUp()){             System.out.println("进行化妆");         }         eatBreakFast();//吃早餐         hook();//挂钩     }     /**是否需要化妆方法*/     protected boolean isMakeUp(){         return false;     }     /**穿衣服*/     protected abstract void dressing();     /**刷牙*/     protected void brushTeeth(){         System.out.println("刷牙");     }     /**洗脸*/     protected void washFace(){         System.out.println("洗脸");     }     /**吃早餐*/     protected abstract void eatBreakFast();     /**挂钩*/     protected void hook(){}; } 

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

/**  * 女生  * 具体实现类  */ public class WomanPerson extends AbstractPerson{     @Override     protected void dressing() {         System.out.println("穿休闲衣服");     }     @Override     protected void eatBreakFast() {         System.out.println("在家弄点吃的,或者在外面买一点小吃");     }     @Override     protected boolean isMakeUp() {         return true;     } } 

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

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

三、应用

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

HttpServlet 的部分源码如下:

public abstract class HttpServlet extends GenericServlet {     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {         // ...     }     protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {         // ...     }     protected void doHead(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {         // ...     }     protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {         // ...     }     protected void doDelete(HttpServletRequest req,  HttpServletResponse resp) throws ServletException, IOException {         // ...     }     protected void doOptions(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {         // ...     }     protected void doTrace(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {         // ...     }     protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {         String method = req.getMethod();         if (method.equals(METHOD_GET)) {             long lastModified = getLastModified(req);             if (lastModified == -1) {                 // servlet doesnt support if-modified-since, no reason                 // to go through further expensive logic                 doGet(req, resp);             } else {                 long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);                 if (ifModifiedSince < lastModified) {                     // If the servlet mod time is later, call doGet()                     // Round down to the nearest second for a proper compare                     // A ifModifiedSince of -1 will always be less                     maybeSetLastModified(resp, lastModified);                     doGet(req, resp);                 } else {                     resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);                 }             }         } else if (method.equals(METHOD_HEAD)) {             long lastModified = getLastModified(req);             maybeSetLastModified(resp, lastModified);             doHead(req, resp);         } else if (method.equals(METHOD_POST)) {             doPost(req, resp);         } else if (method.equals(METHOD_PUT)) {             doPut(req, resp);         } else if (method.equals(METHOD_DELETE)) {             doDelete(req, resp);         } else if (method.equals(METHOD_OPTIONS)) {             doOptions(req,resp);         } else if (method.equals(METHOD_TRACE)) {             doTrace(req,resp);         } else {             //             // Note that this means NO servlet supports whatever             // method was requested, anywhere on this server.             //             String errMsg = lStrings.getString("http.method_not_implemented");             Object[] errArgs = new Object[1];             errArgs[0] = method;             errMsg = MessageFormat.format(errMsg, errArgs);             resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);         }     }     // ...省略... } 

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

import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class HelloWorld extends HttpServlet {   public void init() throws ServletException {     // ...   }   public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {       response.setContentType("text/html");       PrintWriter out = response.getWriter();       out.println("<h1>Hello World!</h1>");   }   public void destroy() {       // ...   } } 

四、总结

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

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

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

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

(责任编辑:人工智能)

推荐内容
  • 电脑错误恢复(快速修复电脑无法进入桌面的常见故障)
  • 电脑新手小白攻略教程(全面解析电脑使用技巧,帮助小白迅速掌握电脑操作)
  • 电脑屏幕手动维修教程(简单易行的DIY维修,让你的电脑屏幕焕然一新)
  • 电脑网络链接错误怎么办?(解决常见电脑网络链接错误的方法与技巧)
  • 大多数 Linux 发行版都会提供一个可以从 USB 启动的 live 环境,以便用户无需安装即可测试系统。我们可以用它来评测这个发行版或仅仅是当成一个一次性系统,并且很容易将这些文件复制到一个 U 盘上,在某些情况下,我们可能需要经常运行同一个或不同的 ISO 镜像。GRUB 2 可以配置成直接从启动菜单运行一个 live 环境,而不需要烧录这些 ISO 到硬盘或 USB 设备。获取和检查可启动的 ISO 镜像当镜像下载完后,我们应该通过 MD5 校验检查它的完整性。这会输出一大串数字与字母合成的序列。将这个序列与下载页提供的 MD5 校验码进行比较,两者应该完全相同。配置 GRUB 2在下面的例子中,一个 Kubuntu 15.04 live 环境将被配置到 Ubuntu 14.04 机器的 Grub 启动菜单项。这应该能在大多数新的以 Ubuntu 为基础的系统上运行。假如你是其它系统并且想实现一些其它的东西,你可以从这些文件了解更多细节,但这会要求你拥有一点 GRUB 使用经验。这个例子的文件 kubuntu-15.04-desktop-amd64.iso 放在位于 /dev/sda1 的 /home/maketecheasier/TempISOs/ 上。为了使 GRUB 2 能正确找到它,我们应该编辑/etc/grub.d40-custom分析上述代码下一行是指定回环设备,且必须给出正确的分区号码。复制代码代码如下:GRUB 的命名在这里稍微有点困惑,对于硬盘来说,它从 “0” 开始计数,第一块硬盘为 #0 ,第二块为 #1 ,第三块为 #2 ,依此类推。但是对于分区来说,它从 “1” 开始计数,第一个分区为 #1 ,第二个分区为 #2 ,依此类推。也许这里有一个很好的原因,但肯定不是明智的(明显用户体验很糟糕)..在 Linux 中第一块硬盘,第一个分区是 /dev/sda1 ,但在 GRUB2 中则是 hd0,1 。第二块硬盘,第三个分区则是 hd1,3, 依此类推.下一个重要的行是:复制代码代码如下:最后复制代码代码如下:启动 live 系统复制代码代码如下:当重启系统后,应该可以看见一个新的、并且允许我们启动刚刚配置的 ISO 镜像的 GRUB 条目:选择这个新条目就允许我们像从 DVD 或 U 盘中启动一个 live 环境一样。
  • 解决电脑Java平台错误的有效方法(探索如何应对和修复常见的电脑Java平台错误)