设计模式:行为型-模板模式

古城微笑少年丶 2022-11-17 13:50 244阅读 0赞

目录

  • 第一章 模板模式介绍
  • 第二章 模板模式实现
    • 2.1、抽象父类
    • 2.2、具体子类
    • 2.3、测试类
  • 第三章 模板模式应用

项目地址:https://gitee.com/caochenlei/design-pattern

第一章 模板模式介绍

模板模式的介绍:

在面向对象程序设计过程中,程序员常常会遇到这种情况:设计一个系统时知道了算法所需的关键步骤,而且确定了这些步骤的执行顺序,但某些步骤的具体实现还未知,或者说某些步骤的实现与具体的环境相关。

例如,去银行办理业务一般要经过以下4个流程:取号、排队、办理具体业务、对银行工作人员进行评分等,其中取号、排队和对银行工作人员进行评分的业务对每个客户是一样的,可以在父类中实现,但是办理具体业务却因人而异,它可能是存款、取款或者转账等,可以延迟到子类中实现。

这样的例子在生活中还有很多,例如,一个人每天会起床、吃饭、做事、睡觉等,其中“做事”的内容每天可能不同。我们把这些规定了流程或格式的实例定义成模板,允许使用者根据自己的需求去更新它,例如,简历模板、论文模板、Word 中模板文件等。

在模板模式(Template Pattern)中,一个抽象类公开定义了执行它的方法的方式/模板,它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。这种类型的设计模式属于行为型模式。

模板模式的优点:

  • 它把认为是不变部分的算法封装到父类中实现,而把可变部分算法由子类继承实现,便于子类继续扩展。
  • 它在父类中提取了公共的部分代码,便于代码复用。
  • 部分方法是由子类实现的,因此子类可以通过扩展方式增加相应的功能,符合开闭原则。

模板模式的缺点:

  • 对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象,间接地增加了系统的复杂度。
  • 父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了代码阅读的难度。
  • 由于继承关系自身的缺点,如果父类添加新的抽象方法,则所有子类都要改一遍。

模板模式的场景:

  • 算法的整体步骤很固定,但其中个别部分易变时,这时候可以使用模板方法模式,将容易变的部分抽象出来,供子类实现。
  • 当多个子类存在公共的行为时,可以将其提取出来并集中到一个公共父类中以避免代码重复。首先,要识别现有代码中的不同之处,并且将不同之处分离为新的操作。最后,用一个调用这些新的操作的模板方法来替换这些不同的代码。
  • 当需要控制子类的扩展时,模板方法只在特定点调用钩子操作,这样就只允许在这些点进行扩展。

模板模式的角色:

抽象父类/抽象模板(Abstract Class): 抽象模板类,负责给出一个算法的轮廓和骨架,它由一个模板方法和若干个基本方法构成。

  • 模板方法:定义了算法的骨架,按某种顺序调用其包含的基本方法。
  • 基本方法:是整个算法中的一个步骤,包含以下几种类型。

    • 抽象方法:在抽象类中给出声明,由具体子类实现。
    • 具体方法:在抽象类中已经实现,在具体子类中可以继承或重写它。
    • 钩子方法:在抽象类中已经实现,包括用于判断的逻辑方法和需要子类重写的空方法两种。

具体子类/具体实现(Concrete Class): 具体实现类,实现抽象类中所定义的抽象方法和钩子方法,它们是一个顶级逻辑的组成步骤。

2ddcafd783df023abd92bf86fcde99f3.gif

第二章 模板模式实现

2.1、抽象父类

Game

  1. public abstract class Game {
  2. abstract void initialize();
  3. abstract void startPlay();
  4. abstract void endPlay();
  5. //模板
  6. public final void play() {
  7. //初始化游戏
  8. initialize();
  9. //开始游戏
  10. startPlay();
  11. //结束游戏
  12. endPlay();
  13. }
  14. }

2.2、具体子类

Football

  1. public class Football extends Game {
  2. @Override
  3. void initialize() {
  4. System.out.println("Football Game Initialized.");
  5. }
  6. @Override
  7. void startPlay() {
  8. System.out.println("Football Game Started.");
  9. }
  10. @Override
  11. void endPlay() {
  12. System.out.println("Football Game Finished.");
  13. }
  14. }

Basketball

  1. public class Basketball extends Game {
  2. @Override
  3. void initialize() {
  4. System.out.println("Basketball Game Initialized.");
  5. }
  6. @Override
  7. void startPlay() {
  8. System.out.println("Basketball Game Started.");
  9. }
  10. @Override
  11. void endPlay() {
  12. System.out.println("Basketball Game Finished.");
  13. }
  14. }

2.3、测试类

Client

  1. public class Client {
  2. public static void main(String[] args) {
  3. Game g1 = new Football();
  4. g1.play();
  5. Game g2 = new Basketball();
  6. g2.play();
  7. }
  8. }
  9. Football Game Initialized.
  10. Football Game Started.
  11. Football Game Finished.
  12. Basketball Game Initialized.
  13. Basketball Game Started.
  14. Basketball Game Finished.

第三章 模板模式应用

在 Servlet 中,每一个 Servlet 类都必须要实现 HttpServlet 接口。GenericServlet 是个通用的、不特定于任何协议的 Servlet,它实现了 Servlet 接口。而 HttpServlet 继承于 GenericServlet,为 Servlet 接口提供了处理 HTTP 协议的通用实现,所以我们定义 Servlet 时只需要继承 HttpServlet 即可。

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

在 HttpServlet 的 service 方法中,首先获取请求的方法名,然后根据方法名调用对应的 doXXX 方法,比如说请求方法为 GET,那么就去调用 doGet 方法;请求方法为 POST,那么就去调用 doPost 方法。

这里,HttpServlet 就相当于定义了一套处理 HTTP 请求的模板。service 方法为模板方法,定义了处理 HTTP 请求的基本流程;doXXX 等方法为基本方法,根据请求方法做相应的处理,子类可重写这些方法。HttpServletRequest 中的 Method 则起到钩子方法的作用。

比如实现一个输出 Hello World 的简单 Servlet,代码如下:

  1. public class HelloWorld extends HttpServlet {
  2. public void doGet(HttpServletRequest request, HttpServletResponse response)
  3. throws ServletException, IOException
  4. {
  5. response.setContentType("text/html");
  6. PrintWriter out = response.getWriter();
  7. out.println("<h1>Hello World!</h1>");
  8. }
  9. }

发表评论

表情:
评论列表 (有 0 条评论,244人围观)

还没有评论,来说两句吧...

相关阅读

    相关 设计模式行为模板方法模式

    模板方法模式在一个方法中定义一个算法骨架,并将某些步骤推迟到子类中实现。模板方法模式可以让子类在不改变算法整体结构的情况下,重新定义算法中的某些步骤。属于行为性设计模式。 这

    相关 行为--模板模式

    概念 -------------------- 用一个抽象类定义一些方法作为实现某个功能的模板(一般设为final),并将模板方法中某些步骤的实现推迟到子类中,使子类可