Design patterns to enjoy with frequently used Java libraries --Template Method patterns

GoF design patterns are also hidden in the Java libraries that you use most often. It's easy to overlook the busy daily work, but once in a while, let's take a closer look at the beautiful design, which can be said to be a kind of art.

This art

source file

HelloServlet class


import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
...

public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html;charset=UTF-8");
        try (PrintWriter out = response.getWriter()) {
            out.println("<html>");
            out.println("<body>");
            out.println("<h1>Hello, world!</h1>");
            out.println("</body>");
            out.println("</html>");
        }
    }
}

Execution result

HelloWorld.png

This is a Java Servlet Hello World program that anyone who has used Java on the server side may have seen it once. If you look at it again, you will be overwhelmed by the source code that strongly insists on object orientation from the beginning.

Points of appreciation

In Java Servlet, you can respond to HTTP GET requests simply by inheriting the HttpServlet class and overriding the doGet method. (The XML file needs a description that maps the class to the URL.)

The above code seems to be concisely organized for code that returns an HTTP response without using a web framework. There seems to be a secret in class inheritance and method overriding, but what is the design? Let's explore together.

When not using the Template Method pattern

The code at the beginning uses the Template Method pattern. First, let's think about what would happen if we didn't use the Template Method pattern.

In Java Servlet, when there is an HTTP request, the service method of the class that implements the Servlet interface corresponding to the URL is called. Therefore, if you implement the service method of the Servlet interface in your own class that does not have a parent class as shown below, it will work without problems.

HelloServlet class


...
public class HelloServlet implements Servlet {
    @Override
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        HttpServletRequest request =  (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;

        //Separate processing by request method
        String method = request.getMethod();
        if (method.equals(METHOD_GET)) {
            //The process is divided according to whether the header of the last update date and time is added.
            long lastModified = getLastModified(req);
            if (lastModified == -1) {
                //When the header of the last update date and time is not added
                //Create HTML
                response.setContentType("text/html;charset=UTF-8");
                try (PrintWriter out = response.getWriter()) {
                    out.println("<html>");
                    out.println("<body>");
                    out.println("<h1>Hello, world!</h1>");
                    out.println("</body>");
                    out.println("</html>");
                }
            } else {
                //When the header of the last update date and time is given
                long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                if (ifModifiedSince < lastModified) {
                    maybeSetLastModified(response, lastModified);

                    //Create HTML
                    response.setContentType("text/html;charset=UTF-8");
                    try (PrintWriter out = response.getWriter()) {
                        out.println("<html>");
                        out.println("<body>");
                        out.println("<h1>Hello, world!</h1>");
                        out.println("</body>");
                        out.println("</html>");
                    }
                } else {
                    //If there is no update from the last time, only the status code is returned
                    response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                }
            }

        } else if (method.equals(METHOD_HEAD)) {
      ...

To register this class as a Servlet, write a URL-class name mapping in the XML file.

web/WEB-INF/web.xml


<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
    <servlet>
        <servlet-name>hello</servlet-name>
        <servlet-class>HelloServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>
    <session-config>
        <session-timeout>30</session-timeout>
    </session-config>
</web-app>

Then, if you access with the URL of domain name / hello, the above HelloServlet # service will be called. The execution result is the same as the image at the beginning.

If the function of this system is only to display "Hello, world!", The above implementation is fine, but when new functions are needed, it becomes more and more painful. As a test, add a ByeServlet class with new functionality.

ByeServlet class


...
public class ByeServlet implements Servlet {
  @Override
  public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
      HttpServletRequest request =  (HttpServletRequest) req;
      HttpServletResponse response = (HttpServletResponse) res;

      //Separate processing by request method
      String method = request.getMethod();
      if (method.equals(METHOD_GET)) {
          //The process is divided according to whether the header of the last update date and time is added.
          long lastModified = getLastModified(req);
          if (lastModified == -1) {
              //When the header of the last update date and time is not added
              //Create HTML
              response.setContentType("text/html;charset=UTF-8");
              try (PrintWriter out = response.getWriter()) {
                  out.println("<html>");
                  out.println("<body>");
                  out.println("<h1>Bye, world!</h1>");
                  out.println("</body>");
                  out.println("</html>");
              }
          } else {
              //When the header of the last update date and time is given
              long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
              if (ifModifiedSince < lastModified) {
                  maybeSetLastModified(response, lastModified);

                  //Create HTML
                  response.setContentType("text/html;charset=UTF-8");
                  try (PrintWriter out = response.getWriter()) {
                      out.println("<html>");
                      out.println("<body>");
                      out.println("<h1>Bye, world!</h1>");
                      out.println("</body>");
                      out.println("</html>");
                  }
              } else {
                  //If there is no update from the last time, only the status code is returned
                  response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
              }
          }

      } else if (method.equals(METHOD_HEAD)) {
      ...

web/WEB-INF/web.xml (additional)


...
  <!--Additions-->
  <servlet>
      <servlet-name>bye</servlet-name>
      <servlet-class>ByeServlet</servlet-class>
  </servlet>
  <servlet-mapping>
      <servlet-name>bye</servlet-name>
      <url-pattern>/bye</url-pattern>
  </servlet-mapping>
...

If you access with the URL of domain name / bye, ByeServlet will work and the browser will display the following.

ByeWorld.png

Now let's compare the two Servlets HelloServlet and ByeServlet. The processing flow is ** both ** as follows.

Process flow

  1. Separate processing by request method (GET / POST, etc.)
  2. Confirm that the header of the last update date and time is added.
  3. Returns HTML if not granted
  4. If granted and updated, return HTML
  5. If it is granted and there is no update, only the status code is returned

The only difference between the two classes is the content of "returning HTML", the others are exactly the same. ** Although the processing flow is the same, the source code is duplicated, and it looks like it has been copied and reused **. It's a painful implementation, and it's not beautiful.

When using the Template Method pattern

Now let's apply the Template Method pattern.

Common processing between classes is implemented in the parent class, and only different processing is implemented in the child class. In this example, "common processing" is to divide the flow according to the header of the last update date and time, and "different processing" is to generate HTML.

First is the parent class.

java:Parent class implementation (javax.servlet.http.HttpServlet class)


package javax.servlet.http;
...
public abstract class HttpServlet extends GenericServlet {
    ...
    /**
     *Methods to override in child classes
     */
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //Since it is supposed to be overridden in the child class, it will be an error if it passes here
    }

    /**
     *Template method
     */
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String method = req.getMethod();
        if (method.equals(METHOD_GET)) {
            //Processing changes depending on whether the header of the last update date and time is added
            long lastModified = getLastModified(req);
            if (lastModified == -1) {
                //When the header of the last update date and time is not added
                //Create HTML
                doGet(req, resp);
            } else {
                //When the header of the last update date and time is given
                long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                if (ifModifiedSince < lastModified) {
                    maybeSetLastModified(resp, lastModified);

                    //Create HTML
                    doGet(req, resp);
                } else {
                    //Returns only status code
                    resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                }
            }

        } else if (method.equals(METHOD_HEAD)) {
        ...

The above is not the code I wrote, but the code of the Java Servlet standard servlet class javax.servlet.http.HttpServlet as it is. When creating Java Servlet, usually inherit this class and implement the process corresponding to HTTP request method (GET / POST etc.) in the child class. It will be as follows.

Implementation of child class (HelloServlet class)


...
public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //Create HTML
        response.setContentType("text/html;charset=UTF-8");
        try (PrintWriter out = response.getWriter()) {
            out.println("<html>");
            out.println("<body>");
            out.println("<h1>Hello, world!</h1>");
            out.println("</body>");
            out.println("</html>");
        }
    }
}

Implementation of child class (ByeServlet class)


...
public class ByeServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //Create HTML
        response.setContentType("text/html;charset=UTF-8");
        try (PrintWriter out = response.getWriter()) {
            out.println("<html>");
            out.println("<body>");
            out.println("<h1>Bye, world!</h1>");
            out.println("</body>");
            out.println("</html>");
        }
    }
}

In this way, by grouping common processes between classes in the parent class (HttpServlet) and describing different processes in the methods (doGet) of each child class, ** there is no duplication of source code. It was **. Also, when adding a new function (Servlet), it is only necessary to inherit the parent class and implement one method, so ** anyone can easily implement it **. Compared to before applying the pattern, it looks beautiful.

The GoF definition of the Template Method pattern is "** Define the skeleton of the algorithm in an operation, leave the definition of some processing to the subclass, and it is included in it without changing the structure of the algorithm. It is [^ 1] ** "to redefine the process. The "algorithm structure" here corresponds to the logic performed in the parent class, which divides the processing by the HTTP method, divides the processing according to the header of the last update date and time, and returns HTML and status code. Also, "redefining the process" corresponds to the part that returns HTML, which is done in the child class.

Expert comments on the Template Method pattern

Many experts have also commented on evaluating the Template Method pattern.

lang_and_engine

What is the Java language designed to create abstract classes for? From this point of view, we can accept this pattern without any resistance.

From List of 23 GoF design patterns to utilize in Java

Hiroshi Yuki

Since the algorithm is described by the template method of the superclass, it is not necessary to describe the algorithm one by one on the subclass side.

["Introduction to Design Patterns Learned in Java Language"](https://www.amazon.co.jp/ Introduction to Design Patterns Learned in Java Language-Yuki-Hiroshi / dp / 4797327030 /)

Finally

It's the real pleasure of programmers to be able to enjoy intellectual enjoyment by just looking at a few lines of code without having to go to the museum.

If you are an engineer who sympathizes with the artistry of the Template Method pattern, please contact the recruiting staff of our company (Qualysite Technologies Inc.). Please contact me!

Related article

Create an instance

-Design pattern to enjoy with Java library used frequently --Factory pattern -Design patterns to enjoy with frequently used Java libraries --Builder patterns -Design patterns to enjoy with frequently used Java libraries --Abstract Factory pattern

Simplify the interface

-Design patterns to enjoy with Java libraries that are often used --Facade patterns -Design pattern to enjoy with frequently used Java library --Adapter pattern

Leave it to another class

--Design patterns to enjoy with frequently used Java libraries --Template Method patterns -Design patterns to enjoy with frequently used Java libraries --Strategy patterns

Reference URL

[^ 1]: ["Design pattern for reuse in object orientation"](https://www.amazon.co.jp/%E3%82%AA%E3%83%96%E3%82%B8 % E3% 82% A7% E3% 82% AF% E3% 83% 88% E6% 8C% 87% E5% 90% 91% E3% 81% AB% E3% 81% 8A% E3% 81% 91% E3 % 82% 8B% E5% 86% 8D% E5% 88% A9% E7% 94% A8% E3% 81% AE% E3% 81% 9F% E3% 82% 81% E3% 81% AE% E3% 83 % 87% E3% 82% B6% E3% 82% A4% E3% 83% B3% E3% 83% 91% E3% 82% BF% E3% 83% BC% E3% 83% B3-% E3% 82% AC% E3% 83% B3% E3% 83% 9E-% E3% 82% A8% E3% 83% AA% E3% 83% 83% E3% 82% AF / dp / 479731126 / ref = sr_1_1? Ie = UTF8 & qid = 1495503419 & sr = 8-1 & keywords =% E3% 82% AA% E3% 83% 96% E3% 82% B8% E3% 82% A7% E3% 82% AF% E3% 83% 88% E6% 8C% 87% E5% 90% 91% E3% 81% AB% E3% 81% 8A% E3% 81% 91% E3% 82% 8B% E5% 86% 8D% E5% 88% A9% E7% 94% A8% E3% 81% AE% E3% 81% 9F% E3% 82% 81% E3% 81% AE% E3% 83% 87% E3% 82% B6% E3% 82% A4% E3% 83% B3% E3% 83% From 91% E3% 82% BF% E3% 83% BC% E3% 83% B3)

Recommended Posts

Design patterns to enjoy with frequently used Java libraries --Template Method patterns
Design patterns to enjoy with frequently used Java libraries --Adapter patterns
Design patterns to enjoy with frequently used Java libraries --Strategy patterns
Design patterns to enjoy with frequently used Java libraries --Facade pattern
Design patterns learned with Java & PHP (summary)
Design patterns to enjoy with frequently used Java libraries --Adapter patterns
Design patterns to enjoy with frequently used Java libraries --Strategy patterns
Design patterns to enjoy with frequently used Java libraries --Template Method patterns
Design patterns to enjoy with frequently used Java libraries --Facade pattern
Design patterns to enjoy with frequently used Java libraries --Abstract Factory pattern
Design patterns learned with Java & PHP (summary)
Docker. Set frequently used commands to alias "with explanation"
[Updated from time to time] Summary of design patterns in Java
Summary of Chapter 2 of Introduction to Design Patterns Learned in Java Language
Chapter 4 Summary of Introduction to Design Patterns Learned in Java Language
Summary of Chapter 3 of Introduction to Design Patterns Learned in Java Language
Docker. Set frequently used commands to alias "with explanation"
Design Pattern #Template Method
[Updated from time to time] Summary of design patterns in Java
Template Method pattern in Java
Python Design Pattern --Template method
Summary of Chapter 2 of Introduction to Design Patterns Learned in Java Language
Chapter 4 Summary of Introduction to Design Patterns Learned in Java Language
Summary of Chapter 3 of Introduction to Design Patterns Learned in Java Language
I tried to summarize the frequently used implementation method of pytest-mock