it-swarm.com.de

Design Patterns webbasierte Anwendungen

Ich entwerfe eine einfache webbasierte Anwendung. Ich bin neu in dieser webbasierten Domain. Ich brauchte Ihren Rat bezüglich der Entwurfsmuster, wie die Verantwortung auf Servlets verteilt werden soll, Kriterien, um ein neues Servlet zu erstellen usw.

Tatsächlich befinden sich auf meiner Homepage nur wenige Entitäten, und für jede dieser Entitäten stehen einige Optionen zur Verfügung, z. B. Hinzufügen, Bearbeiten und Löschen. Früher verwendete ich ein Servlet pro Option wie Servlet1 zum Hinzufügen von Entity1, Servlet2 zum Bearbeiten von Entity1 usw. Auf diese Weise hatten wir eine große Anzahl von Servlets.

Jetzt ändern wir unser Design. Meine Frage ist, wie Sie genau wählen, wie Sie die Verantwortung eines Servlets wählen. Sollten wir ein Servlet pro Entität haben, das alle seine Optionen verarbeitet und Anfragen an die Serviceebene weiterleitet. Oder sollten wir ein Servlet für die gesamte Seite haben, das die Anforderung der gesamten Seite verarbeitet und sie dann an die entsprechende Serviceebene weiterleitet? Außerdem sollte das Anforderungsobjekt an die Serviceschicht weitergeleitet werden oder nicht.

356
mawia

Eine etwas anständige Webanwendung besteht aus einer Mischung von Designmustern. Ich werde nur die wichtigsten erwähnen.


Model View Controller-Muster

Das zentrale (architektonische) Entwurfsmuster, das Sie verwenden möchten, ist das Model-View-Controller-Muster . Der Controller soll durch ein Servlet dargestellt werden, das (in) direkt ein bestimmtes Model und View basierend auf der Anfrage erstellt/verwendet. Das Modell soll durch Javabean-Klassen dargestellt werden. Dies lässt sich häufig weiter unterteilen in Geschäftsmodell, das die Aktionen (Verhalten) enthält, und Datenmodell, das die Daten (Informationen) enthält. Das View soll durch JSP-Dateien dargestellt werden, die direkten Zugriff auf das (Data) Model durch EL (Expression Language) haben.

Dann gibt es Variationen, die davon abhängen, wie Aktionen und Ereignisse behandelt werden. Die beliebtesten sind:

  • Request (action) based MVC : Dies ist am einfachsten zu implementieren. Das (Business) Model arbeitet direkt mit HttpServletRequest und HttpServletResponse Objekten. Sie müssen die Anforderungsparameter (meistens) selbst erfassen, konvertieren und validieren. Das View kann durch einfaches Vanilla HTML/CSS/JS dargestellt werden und behält den Status nicht über Anforderungen hinweg bei. So funktioniert unter anderem Spring MVC , Struts und Stripes .

  • Komponentenbasierte MVC : Dies ist schwieriger zu implementieren. Am Ende haben Sie jedoch ein einfacheres Modell und eine einfachere Ansicht, in der alle "rohen" Servlet-APIs vollständig entfernt sind. Sie müssen die Anforderungsparameter nicht selbst erfassen, konvertieren und validieren. Der Controller führt diese Aufgabe aus und legt die erfassten, konvertierten und validierten Anforderungsparameter im Model fest. Sie müssen lediglich Aktionsmethoden definieren, die direkt mit den Modelleigenschaften zusammenarbeiten. Das View wird durch "Komponenten" im Stil von JSP-Taglibs oder XML-Elementen dargestellt, die wiederum HTML/CSS/JS generieren. Der Status von View für die nachfolgenden Anforderungen wird in der Sitzung beibehalten. Dies ist besonders hilfreich für serverseitige Konvertierungs-, Validierungs- und Wertänderungsereignisse. So funktioniert unter anderem JSF , Wicket und Play! .

Nebenbei bemerkt ist das Herumhasten mit einem selbst entwickelten MVC-Framework eine sehr gute Lernübung, und ich empfehle sie, solange Sie sie für persönliche/private Zwecke aufbewahren. Wenn Sie jedoch erst einmal professionell sind, wird dringend empfohlen, ein vorhandenes Framework auszuwählen, anstatt Ihr eigenes neu zu erfinden. Das Erlernen eines vorhandenen und gut entwickelten Frameworks erfordert langfristig weniger Zeit als das Entwickeln und Verwalten eines robusten Frameworks.

In der folgenden detaillierten Erläuterung beschränke ich mich auf die Anforderung von MVC, da dies einfacher zu implementieren ist.


Front Controller-Muster ( Mediator-Muster )

Zunächst sollte der Teil Controller das Front-Controller-Muster (das eine spezielle Art von Mediator-Muster ist) implementieren. Es sollte nur aus einem einzigen Servlet bestehen, das einen zentralen Einstiegspunkt für alle Anforderungen bietet. Es sollte das Modell auf der Grundlage von Informationen erstellen, die von der Anforderung zur Verfügung gestellt werden, z. B. der Pfadinfo oder der Servletpath, die Methode und/oder bestimmte Parameter. Das Geschäftsmodell wird im folgenden Action Beispiel HttpServlet genannt.

protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    try {
        Action action = ActionFactory.getAction(request);
        String view = action.execute(request, response);

        if (view.equals(request.getPathInfo().substring(1)) {
            request.getRequestDispatcher("/WEB-INF/" + view + ".jsp").forward(request, response);
        }
        else {
            response.sendRedirect(view); // We'd like to fire redirect in case of a view change as result of the action (PRG pattern).
        }
    }
    catch (Exception e) {
        throw new ServletException("Executing action failed.", e);
    }
}

Das Ausführen der Aktion sollte einen Bezeichner zurückgeben, um die Ansicht zu lokalisieren. Am einfachsten wäre es, ihn als Dateinamen der JSP zu verwenden. Ordnen Sie dieses Servlet einem bestimmten url-pattern In web.xml Zu, z. /pages/*, *.do Oder auch nur *.html.

Bei Präfixmustern wie zum Beispiel /pages/* Können Sie dann URLs wie http://example.com/pages/register , http: // example) aufrufen. com/pages/login , etc und versorge /WEB-INF/register.jsp, /WEB-INF/login.jsp mit den entsprechenden GET- und POST Aktionen. Die Teile register , login usw. sind dann wie im obigen Beispiel über request.getPathInfo() verfügbar.

Wenn Sie Suffix-Muster wie *.do, *.html Usw. verwenden, können Sie dann URLs wie http://example.com/register.do aufrufen , http://example.com/login.do , etc und Sie sollten die Codebeispiele in dieser Antwort (auch das ActionFactory) ändern, um das register zu extrahieren und login teile stattdessen durch request.getServletPath() .


Strategiemuster

Das Action sollte dem Strategiemuster folgen. Es muss als Abstrakt-/Schnittstellentyp definiert werden, der die Arbeit auf der Grundlage der Argumente übergeben der abstrakten Methode erledigen soll (dies ist der Unterschied zu Befehlsmuster , wobei der Abstract/Interface-Typ die Arbeit auf der Grundlage der Argumente erledigen soll, die während der Erstellung der Implementierung übergeben wurden).

public interface Action {
    public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception;
}

Möglicherweise möchten Sie Exception mit einer benutzerdefinierten Ausnahme wie ActionException spezifizieren. Es ist nur ein einfaches Kickoff-Beispiel, der Rest liegt ganz bei Ihnen.

Hier ist ein Beispiel für ein LoginAction, das (wie der Name schon sagt) den Benutzer anmeldet. Das User selbst ist wiederum ein Datenmodell. Das View erkennt das Vorhandensein des User.

public class LoginAction implements Action {

    public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception {
        String username = request.getParameter("username");
        String password = request.getParameter("password");
        User user = userDAO.find(username, password);

        if (user != null) {
            request.getSession().setAttribute("user", user); // Login user.
            return "home"; // Redirect to home page.
        }
        else {
            request.setAttribute("error", "Unknown username/password. Please retry."); // Store error message in request scope.
            return "login"; // Go back to redisplay login form with error.
        }
    }

}

Factory-Methodenmuster

Das ActionFactory sollte dem Factory-Methodenmuster folgen. Grundsätzlich sollte eine Erstellungsmethode bereitgestellt werden, die eine konkrete Implementierung eines Abstrakt-/Schnittstellentyps zurückgibt. In diesem Fall sollte eine Implementierung der Schnittstelle Action basierend auf den in der Anforderung angegebenen Informationen zurückgegeben werden. Beispiel: Methode und Pfadinfo (Die Pfadinfo ist der Teil nach dem Kontext- und Servlet-Pfad in der Anforderungs-URL, ohne die Abfragezeichenfolge).

public static Action getAction(HttpServletRequest request) {
    return actions.get(request.getMethod() + request.getPathInfo());
}

Das actions sollte wiederum ein statisches/anwendungsweites Map<String, Action> Sein, das alle bekannten Aktionen enthält. Es liegt an Ihnen, wie Sie diese Karte füllen. Hardcoding:

actions.put("POST/register", new RegisterAction());
actions.put("POST/login", new LoginAction());
actions.put("GET/logout", new LogoutAction());
// ...

Oder konfigurierbar basierend auf einer Properties/XML-Konfigurationsdatei im Klassenpfad: (Pseudo)

for (Entry entry : configuration) {
    actions.put(entry.getKey(), Class.forName(entry.getValue()).newInstance());
}

Oder dynamisch basierend auf einem Scan im Klassenpfad nach Klassen, die eine bestimmte Schnittstelle und/oder Annotation implementieren: (Pseudo)

for (ClassFile classFile : classpath) {
    if (classFile.isInstanceOf(Action.class)) {
       actions.put(classFile.getAnnotation("mapping"), classFile.newInstance());
    }
}

Denken Sie daran, ein "Nichts tun" Action für den Fall zu erstellen, dass keine Zuordnung vorhanden ist. Lassen Sie es zum Beispiel direkt die request.getPathInfo().substring(1) dann zurückgeben.


Andere Muster

Das waren die bisher wichtigen Muster.

Um einen Schritt weiter zu kommen, können Sie mit dem Fassadenmuster eine Context -Klasse erstellen, die wiederum die Anforderungs- und Antwortobjekte umschließt und verschiedene praktische Methoden bietet, die an die Anforderungs- und Antwortobjekte delegieren und übergeben Sie das als Argument stattdessen an die Action#execute() -Methode. Dies fügt eine zusätzliche abstrakte Ebene hinzu, um die unformatierte Servlet-API auszublenden. Sie sollten dann grundsätzlich in jeder Action -Implementierung mit null import javax.servlet.* - Deklarationen enden. In JSF-Begriffen ist dies das, was die Klassen FacesContext und ExternalContext tun. Ein konkretes Beispiel finden Sie in diese Antwort .

Dann gibt es das Statusmuster für den Fall, dass Sie eine zusätzliche Abstraktionsebene hinzufügen möchten, um die Aufgaben zum Sammeln der Anforderungsparameter, Konvertieren, Validieren, Aktualisieren der Modellwerte und Ausführen der Aktionen aufzuteilen . In JSF-Begriffen ist dies das, was LifeCycle tut.

Dann gibt es das Zusammengesetztes Muster für den Fall, dass Sie eine komponentenbasierte Ansicht erstellen möchten, die an das Modell angehängt werden kann und deren Verhalten vom Status des anforderungsbasierten Lebenszyklus abhängt. In JSF-Begriffen ist dies die Darstellung von UIComponent .

Auf diese Weise können Sie sich Stück für Stück zu einem komponentenbasierten Framework entwickeln.


Siehe auch:

485
BalusC

In dem geschlagenen MVC-Muster ist das Servlet ein "C" -Controller.

Seine Hauptaufgabe besteht darin, die erste Anforderungsbewertung durchzuführen und dann die Verarbeitung basierend auf der ersten Bewertung an den jeweiligen Mitarbeiter zu senden. Eine der Aufgaben des Arbeiters kann darin bestehen, einige Präsentationsebenen-Beans einzurichten und die Anforderung an die JSP-Seite weiterzuleiten, um HTML zu rendern. Aus diesem Grund müssen Sie das Anforderungsobjekt an die Serviceschicht übergeben.

Ich würde jedoch nicht anfangen, rohe Servlet Klassen zu schreiben. Die Arbeit, die sie machen, ist sehr vorhersehbar und sehr gut. Zum Glück gibt es viele bewährte Kandidaten (in alphabetischer Reihenfolge): Apache Wicket , Java Server Faces , Spring , um a wenige.

13

IMHO, es gibt keinen großen Unterschied bei der Webanwendung, wenn man sie unter dem Gesichtspunkt der Verantwortlichkeitszuweisung betrachtet. Behalten Sie jedoch die Klarheit in der Ebene. Belassen Sie alles nur für den Präsentationszweck in der Präsentationsebene, z. B. das Steuerelement und den Code, die für die Websteuerelemente spezifisch sind. Lassen Sie einfach Ihre Entitäten in der Business-Schicht und alle Funktionen (wie Hinzufügen, Bearbeiten, Löschen) usw. in der Business-Schicht. Rendern Sie sie jedoch auf dem Browser, damit sie in der Präsentationsebene verarbeitet werden können. Für .NET ist das ASP.NET MVC-Muster im Hinblick auf die Trennung der Ebenen sehr gut. Schauen Sie sich das MVC-Muster an.

5
Kangkan

BalusC Ausgezeichnete Antwort deckt die meisten Muster für Webanwendungen ab.

Für einige Anwendungen ist möglicherweise Chain-of-responsibility_pattern erforderlich

Im objektorientierten Entwurf ist das Verantwortungskettenmuster ein Entwurfsmuster, das aus einer Quelle von Befehlsobjekten und einer Reihe von Verarbeitungsobjekten besteht. Jedes Verarbeitungsobjekt enthält eine Logik, die die Typen der Befehlsobjekte definiert, die es verarbeiten kann. Der Rest wird an das nächste Verarbeitungsobjekt in der Kette übergeben.

Use Case zur Verwendung dieses Musters:

Wann der Handler zum Verarbeiten einer Anforderung (eines Befehls) unbekannt ist und diese Anforderung an mehrere Objekte gesendet werden kann. Generell setzen Sie Nachfolger auf Objekt. Wenn das aktuelle Objekt die Anfrage nicht bearbeiten oder teilweise verarbeiten kann, leiten Sie die gleiche Anfrage an das Nachfolger Objekt weiter.

Nützliche SE-Fragen/Artikel:

Warum sollte ich jemals eine Verantwortungskette gegenüber einem Dekorateur verwenden?

Gemeinsame Verwendungen für die Verantwortungskette?

Chain-of-Responsibility-Pattern von oodesign

chain_of_responsibility von sourcemaking

3
Ravindra babu

Ich habe das Struts Framework verwendet und finde es ziemlich einfach zu lernen. Wenn Sie das struts-Framework verwenden, enthält jede Seite Ihrer Site die folgenden Elemente.

1) Eine Aktion, die verwendet wird, wird jedes Mal aufgerufen, wenn die HTML-Seite aktualisiert wird. Die Aktion sollte die Daten im Formular auffüllen, wenn die Seite zum ersten Mal geladen wird, und Interaktionen zwischen der Web-Benutzeroberfläche und der Business-Schicht verarbeiten. Wenn Sie die JSP - Seite zum Ändern eines veränderlichen Java - Objekts verwenden, sollte eine Kopie des Java - Objekts im Formular und nicht im Original gespeichert werden, damit das Die ursprünglichen Daten werden nur geändert, wenn der Benutzer die Seite speichert.

2) Das Formular, mit dem Daten zwischen der Aktion und der JSP-Seite übertragen werden. Dieses Objekt sollte aus einer Reihe von Gettern und Setzern für Attribute bestehen, auf die die JSP-Datei zugreifen muss. Das Formular verfügt auch über eine Methode zum Überprüfen von Daten, bevor diese dauerhaft gespeichert werden.

3) Eine JSP-Seite, die zum Rendern des endgültigen HTML-Codes der Seite verwendet wird. Die JSP-Seite ist eine Mischung aus HTML- und speziellen Struts-Tags, mit denen auf Daten im Formular zugegriffen und diese bearbeitet werden. Obwohl Struts Benutzern das Einfügen von Java Code in JSP-Dateien ermöglicht, sollten Sie sehr vorsichtig sein, da dies das Lesen Ihres Codes erschwert. Java Code in JSP-Dateien) JSP-Dateien sind schwierig zu debuggen und können nicht Unit-getestet werden.Wenn Sie feststellen, dass Sie mehr als 4-5 Zeilen Java Code in eine JSP-Datei schreiben, sollte der Code wahrscheinlich in die Aktion verschoben werden.

3