[GO] Entwerfen Sie Muster für häufig verwendete Java-Bibliotheken - Muster der Vorlagenmethode

GoF-Entwurfsmuster sind auch in den Java-Bibliotheken versteckt, die Sie am häufigsten verwenden. Es ist leicht, die geschäftige tägliche Arbeit zu übersehen, aber ab und zu genießen wir das schöne Design, das man als eine Art Kunst bezeichnen kann.

Diese Kunst

Quelldatei

HelloServlet-Klasse


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>");
        }
    }
}

Ausführungsergebnis

HelloWorld.png

Ein Java Servlet Hello World-Programm, das jeder, der Java auf der Serverseite verwendet hat, möglicherweise einmal gesehen hat. Wenn Sie es sich noch einmal ansehen, werden Sie von dem Quellcode überwältigt sein, der von Anfang an stark auf Objektorientierung besteht.

Anerkennungspunkte

Java Servlet kann HTTP-GET-Anforderungen verarbeiten, indem es einfach die Klasse "HttpServlet" erbt und die Methode "doGet" überschreibt. (Die XML-Datei muss eine Beschreibung enthalten, die die Klasse der URL zuordnet.)

Der obige Code scheint für Code, der eine HTTP-Antwort ohne Verwendung des WEB-Frameworks zurückgibt, übersichtlich organisiert zu sein. Es scheint ein Geheimnis in der Klassenvererbung und der Methodenüberschreibung zu geben, aber wie sieht das Design aus? Lassen Sie uns gemeinsam erkunden.

Wenn Sie das Muster der Vorlagenmethode nicht verwenden

Der Code am Anfang verwendet das Muster der Vorlagenmethode. Lassen Sie uns zunächst überlegen, was passieren würde, wenn wir das Muster der Vorlagenmethode nicht verwenden würden.

Wenn in Java Servlet eine HTTP-Anforderung vorliegt, wird die "service" -Methode der Klasse aufgerufen, die die der URL entsprechende "Servlet" -Schnittstelle implementiert. Wenn Sie die "service" -Methode der "Servlet" -Schnittstelle in Ihrer eigenen Klasse implementieren, die keine übergeordnete Klasse hat, wie unten gezeigt, funktioniert dies daher problemlos.

HelloServlet-Klasse


...
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 Verarbeitung nach Anforderungsmethode
        String method = request.getMethod();
        if (method.equals(METHOD_GET)) {
            //Der Prozess wird danach aufgeteilt, ob der Header für das Datum und die Uhrzeit der letzten Aktualisierung hinzugefügt wurde.
            long lastModified = getLastModified(req);
            if (lastModified == -1) {
                //Wenn der Header für das Datum und die Uhrzeit der letzten Aktualisierung nicht hinzugefügt wird
                //Erstellen Sie 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 {
                //Wenn der Header für das Datum und die Uhrzeit der letzten Aktualisierung hinzugefügt wird
                long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                if (ifModifiedSince < lastModified) {
                    maybeSetLastModified(response, lastModified);

                    //Erstellen Sie 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 {
                    //Wenn es beim letzten Mal keine Aktualisierung gibt, wird nur der Statuscode zurückgegeben
                    response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                }
            }

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

Um diese Klasse als Servlet zu registrieren, schreiben Sie eine Namenszuordnung für URL-Klassen in die XML-Datei.

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>

Wenn Sie dann mit der URL "Domain Name / Hallo" zugreifen, wird der oben genannte "HelloServlet # Service" aufgerufen. Das Ausführungsergebnis ist das gleiche wie das Bild am Anfang.

Wenn die Funktion dieses Systems nur darin besteht, "Hallo Welt!" Anzuzeigen, ist die obige Implementierung in Ordnung, aber wenn neue Funktionen benötigt werden, wird es immer schmerzhafter. Fügen Sie als Test eine ByeServlet-Klasse mit neuen Funktionen hinzu.

ByeServlet-Klasse


...
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 Verarbeitung nach Anforderungsmethode
      String method = request.getMethod();
      if (method.equals(METHOD_GET)) {
          //Der Prozess wird danach aufgeteilt, ob der Header für das Datum und die Uhrzeit der letzten Aktualisierung hinzugefügt wurde.
          long lastModified = getLastModified(req);
          if (lastModified == -1) {
              //Wenn der Header für das Datum und die Uhrzeit der letzten Aktualisierung nicht hinzugefügt wird
              //Erstellen Sie 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 {
              //Wenn der Header für das Datum und die Uhrzeit der letzten Aktualisierung hinzugefügt wird
              long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
              if (ifModifiedSince < lastModified) {
                  maybeSetLastModified(response, lastModified);

                  //Erstellen Sie 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 {
                  //Wenn es beim letzten Mal keine Aktualisierung gibt, wird nur der Statuscode zurückgegeben
                  response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
              }
          }

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

web/WEB-INF/web.xml (zusätzliche Menge)


...
  <!--Ergänzungen-->
  <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>
...

Wenn Sie mit der URL "Domain Name / Bye" zugreifen, funktioniert "ByeServlet" und der Browser zeigt Folgendes an.

ByeWorld.png

Vergleichen wir nun die beiden Servlets "HelloServlet" und "ByeServlet". Der Verarbeitungsablauf ist ** beide ** wie folgt.

Prozessablauf

  1. Separate Verarbeitung nach Anforderungsmethode (GET / POST usw.)
  2. Stellen Sie sicher, dass der zuletzt geänderte Datums- und Zeitkopf hinzugefügt wurde
  3. Gibt HTML zurück, wenn es nicht gewährt wird
  4. Gibt HTML zurück, wenn es gewährt und aktualisiert wurde
  5. Wenn es gewährt wird und keine Aktualisierung erfolgt, wird nur der Statuscode zurückgegeben

Der einzige Unterschied zwischen den beiden Klassen ist der Inhalt von "HTML zurückgeben", die anderen sind genau gleich. ** Obwohl der Verarbeitungsablauf derselbe ist, wird der Quellcode dupliziert und es sieht so aus, als ob er kopiert und wiederverwendet wurde **. Es ist eine schmerzhafte Implementierung und nicht schön.

Bei Verwendung des Template-Methodenmusters

Wenden wir nun das Muster der Vorlagenmethode an.

Die gemeinsame Verarbeitung zwischen Klassen ist in der übergeordneten Klasse implementiert, und in der untergeordneten Klasse ist nur eine andere Verarbeitung implementiert. In diesem Beispiel besteht "gemeinsame Verarbeitung" darin, den Fluss nach dem Header des Datums und der Uhrzeit der letzten Aktualisierung zu teilen, und "unterschiedliche Verarbeitung" besteht darin, HTML zu generieren.

Erstens ist die Elternklasse.

java:Implementierung der übergeordneten Klasse (javax.servlet.http.HttpServlet-Klasse)


package javax.servlet.http;
...
public abstract class HttpServlet extends GenericServlet {
    ...
    /**
     *Methoden zum Überschreiben in untergeordneten Klassen
     */
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //Da es in der untergeordneten Klasse überschrieben werden soll, ist es ein Fehler, wenn es hier bestanden wird
    }

    /**
     *Vorlagenmethode
     */
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String method = req.getMethod();
        if (method.equals(METHOD_GET)) {
            //Die Verarbeitung ändert sich abhängig davon, ob der Header für das Datum und die Uhrzeit der letzten Aktualisierung hinzugefügt wurde
            long lastModified = getLastModified(req);
            if (lastModified == -1) {
                //Wenn der Header für das Datum und die Uhrzeit der letzten Aktualisierung nicht hinzugefügt wird
                //Erstellen Sie HTML
                doGet(req, resp);
            } else {
                //Wenn der Header für das Datum und die Uhrzeit der letzten Aktualisierung hinzugefügt wird
                long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                if (ifModifiedSince < lastModified) {
                    maybeSetLastModified(resp, lastModified);

                    //Erstellen Sie HTML
                    doGet(req, resp);
                } else {
                    //Gibt nur den Statuscode zurück
                    resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                }
            }

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

Das Obige ist nicht der Code, den ich geschrieben habe, sondern der Code der Java-Servlet-Standard-Servlet-Klasse javax.servlet.http.HttpServlet wie er ist. Beim Erstellen eines Java-Servlets wird diese Klasse normalerweise vererbt und die untergeordnete Klasse implementiert die Verarbeitung entsprechend der HTTP-Anforderungsmethode (GET / POST usw.). Es wird wie folgt sein.

Implementierung einer untergeordneten Klasse (HelloServlet-Klasse)


...
public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //Erstellen Sie 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>");
        }
    }
}

Implementierung einer untergeordneten Klasse (ByeServlet-Klasse)


...
public class ByeServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //Erstellen Sie 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>");
        }
    }
}

Auf diese Weise wird durch Gruppieren gemeinsamer Prozesse zwischen Klassen in einer übergeordneten Klasse (HttpServlet) und Beschreiben verschiedener Prozesse in den Methoden ( doGet) jeder untergeordneten Klasse ** keine Duplizierung des Quellcodes vorgenommen. Es war **. Wenn Sie eine neue Funktion (Servant) hinzufügen, müssen Sie nur die übergeordnete Klasse erben und eine Methode implementieren, damit ** jeder sie problemlos implementieren kann **. Im Vergleich zu vor dem Auftragen des Musters sieht es wunderschön aus.

Die GoF-Definition des Template-Methodenmusters lautet "** Definieren Sie das Grundgerüst des Algorithmus in einer Operation und überlassen Sie die Definition einer Verarbeitung der Unterklasse. Sie ist darin enthalten, ohne die Struktur des Algorithmus zu ändern. Es ist [^ 1] ** ", den Prozess neu zu definieren. Die "Algorithmusstruktur" entspricht hier der in der übergeordneten Klasse ausgeführten Logik, die die Verarbeitung durch die HTTP-Methode teilt, die Verarbeitung nach dem Header des Datums und der Uhrzeit der letzten Aktualisierung unterteilt und HTML und den Statuscode zurückgibt. Außerdem entspricht "Neudefinition des Prozesses" dem Teil, der HTML zurückgibt, was in der untergeordneten Klasse erfolgt.

Expertenmeinungen zum Muster der Vorlagenmethode

Viele Experten haben sich auch zur Bewertung des Musters der Vorlagenmethode geäußert.

lang_and_engine

Was ist der Zweck der Erstellung abstrakter Klassen in der Java-Sprache? Wenn Sie von diesem Punkt lernen, können Sie dieses Muster ohne Widerstand akzeptieren.

Aus Liste von 23 GoF-Entwurfsmustern zur Verwendung in Java

Hiroshi Yuki

Da der Algorithmus durch die Template-Methode der Superklasse beschrieben wird, ist es nicht erforderlich, den Algorithmus einzeln auf der Unterklassenseite zu beschreiben.

["Einführung in in Java-Sprache erlernte Entwurfsmuster"](https://www.amazon.co.jp/ Einführung in in Java-Sprache erlernte Entwurfsmuster-Yuki-Hiroshi / dp / 4797327030 /)

Schließlich

Es ist eine Freude für Programmierer, intellektuellen Genuss zu genießen, indem sie nur ein paar Codezeilen betrachten, ohne ins Museum gehen zu müssen.

Wenn Sie ein Ingenieur sind, der mit der Kunst des Template-Methodenmusters einverstanden ist, wenden Sie sich bitte an den Einstellungsmanager unseres Unternehmens (Qualysite Technologies Co., Ltd.). Bitte kontaktiere mich!

In Verbindung stehender Artikel

Erstellen Sie eine Instanz

Vereinfachen Sie die Schnittstelle

Überlasse es einer anderen Klasse

Referenz-URL

[^ 1]: ["Entwurfsmuster für die Wiederverwendung in der Objektorientierung"](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% Von 91% E3% 82% BF% E3% 83% BC% E3% 83% B3)

Recommended Posts

Entwerfen Sie Muster für häufig verwendete Java-Bibliotheken - Muster der Vorlagenmethode
Entwerfen Sie Muster für häufig verwendete Java-Bibliotheken - Adaptermuster
Entwerfen Sie Muster für häufig verwendete Java-Bibliotheken - Strategiemuster
Entwerfen Sie Muster für häufig verwendete Java-Bibliotheken - Fassadenmuster
Mit Java & PHP erlernte Entwurfsmuster (Zusammenfassung)
Entwerfen Sie Muster für häufig verwendete Java-Bibliotheken - Adaptermuster
Entwerfen Sie Muster für häufig verwendete Java-Bibliotheken - Strategiemuster
Entwerfen Sie Muster für häufig verwendete Java-Bibliotheken - Muster der Vorlagenmethode
Entwerfen Sie Muster für häufig verwendete Java-Bibliotheken - Fassadenmuster
Entwerfen Sie Muster für häufig verwendete Java-Bibliotheken - Abstract Factory-Muster
Mit Java & PHP erlernte Entwurfsmuster (Zusammenfassung)
Docker. Setzen Sie häufig verwendete Befehle auf den Alias "mit Erklärung".
[Von Zeit zu Zeit aktualisiert] Zusammenfassung der Entwurfsmuster in Java
Zusammenfassung von Kapitel 2 der Einführung in Entwurfsmuster, die in Java gelernt wurden
Kapitel 4 Zusammenfassung der Einführung in Entwurfsmuster, die in Java gelernt wurden
Zusammenfassung von Kapitel 3 der Einführung in Entwurfsmuster, die in Java gelernt wurden
Docker. Setzen Sie häufig verwendete Befehle auf den Alias "mit Erklärung".
Entwurfsmuster # Template-Methode
[Von Zeit zu Zeit aktualisiert] Zusammenfassung der Entwurfsmuster in Java
Muster der Vorlagenmethode in Java
Python Design Pattern - Template-Methode
Zusammenfassung von Kapitel 2 der Einführung in Entwurfsmuster, die in Java gelernt wurden
Kapitel 4 Zusammenfassung der Einführung in Entwurfsmuster, die in Java gelernt wurden
Zusammenfassung von Kapitel 3 der Einführung in Entwurfsmuster, die in Java gelernt wurden
Ich habe versucht, die häufig verwendete Implementierungsmethode von pytest-mock zusammenzufassen