368 Stimmen

Entwurfsmuster für webbasierte Anwendungen

Ich entwerfe eine einfache webbasierte Anwendung. Ich bin neu in diesem webbasierten Bereich und benötige Ihren Rat in Bezug auf die Entwurfsmuster, z. B. wie die Verantwortung zwischen Servlets verteilt werden sollte, Kriterien für die Erstellung neuer Servlets usw.

Ich habe einige Entitäten auf meiner Homepage und für jede von ihnen gibt es einige Optionen wie Hinzufügen, Bearbeiten und Löschen. Früher habe ich ein Servlet pro Option verwendet, z.B. Servlet1 zum Hinzufügen von Entity1, Servlet2 zum Bearbeiten von Entity1 und so weiter, und auf diese Weise hatten wir am Ende eine große Anzahl von Servlets.

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

501voto

BalusC Punkte 1034465

Eine halbwegs anständige Webanwendung besteht aus einer Mischung von Entwurfsmustern. Ich werde nur die wichtigsten davon erwähnen.


Model-View-Controller-Muster

Das zentrale (architektonische) Entwurfsmuster, das Sie verwenden möchten, ist das Model-View-Controller-Muster . En Controller soll durch ein Servlet repräsentiert werden, das (un)direkt eine bestimmte Datei erzeugt/verwendet. Modell y Siehe auf der Grundlage der Anfrage. Die Website Modell durch Javabean-Klassen dargestellt werden soll. Dies ist oft weiter unterteilbar in Geschäftsmodell der die Aktionen (Verhalten) enthält und Datenmodell der die Daten (Informationen) enthält. Die Siehe soll durch JSP-Dateien dargestellt werden, die direkten Zugriff auf die ( Daten ) Modell durch EL (Expression Language).

Außerdem gibt es Variationen, die darauf basieren, wie Aktionen und Ereignisse gehandhabt werden. Die beliebtesten sind:

  • Anfrage (Aktion) basierte MVC : Dies ist am einfachsten zu realisieren. Die ( Business ) Modell arbeitet direkt mit HttpServletRequest y HttpServletResponse Objekte. Sie müssen die Anforderungsparameter (meistens) selbst erfassen, konvertieren und validieren. Die Siehe kann durch einfaches HTML/CSS/JS dargestellt werden und behält den Zustand über Anfragen hinweg nicht bei. Dies ist unter anderem der Grund dafür Spring MVC , Streben y Streifen funktioniert.

  • Komponentenbasiertes MVC : Dies ist schwieriger zu realisieren. Aber Sie erhalten ein einfacheres Modell und eine einfachere Ansicht, in der die "rohe" Servlet-API vollständig abstrahiert ist. Sie sollten nicht die Notwendigkeit haben, die Anfrageparameter selbst zu sammeln, zu konvertieren und zu validieren. Die Controller übernimmt diese Aufgabe und setzt die gesammelten, konvertierten und validierten Anfrageparameter in die Modell . Alles, was Sie tun müssen, ist, Aktionsmethoden zu definieren, die direkt mit den Modelleigenschaften arbeiten. Die Siehe wird durch "Komponenten" in Form von JSP-Taglibs oder XML-Elementen dargestellt, die ihrerseits HTML/CSS/JS erzeugen. Der Zustand der Siehe für die nachfolgenden Anfragen wird in der Sitzung beibehalten. Dies ist besonders hilfreich bei serverseitigen Konvertierungs-, Validierungs- und Wertänderungsereignissen. Auf diese Weise wird unter anderem JSF , Wicket y Spielen! funktioniert.

Nebenbei bemerkt ist das Herumhantieren mit einem selbstentwickelten MVC-Framework eine sehr schöne Lernübung, und ich empfehle es, solange Sie es für persönliche/private Zwecke nutzen. Sobald Sie aber professionell arbeiten, sollten Sie lieber ein bestehendes Framework verwenden, als Ihr eigenes neu zu erfinden. Das Erlernen eines bestehenden und gut entwickelten Frameworks nimmt langfristig weniger Zeit in Anspruch als die Entwicklung und Pflege eines eigenen robusten Frameworks.

In der folgenden ausführlichen Erklärung beschränke ich mich auf anforderungsbasiertes MVC, da dies einfacher zu implementieren ist.


Front Controller Muster ( Vermittler-Muster )

Erstens, die Controller Teil sollte die Front Controller Muster (das ist eine spezielle Art von Vermittler-Muster ). Es sollte nur aus einem einzigen Servlet bestehen, das als zentraler Einstiegspunkt für alle Anfragen dient. Es sollte die Modell auf der Grundlage von Informationen, die durch die Anfrage verfügbar sind, wie z. B. Pathinfo oder Servletpath, die Methode und/oder spezifische Parameter. Die Geschäftsmodell heißt Action im Folgenden HttpServlet Beispiel.

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

Die Ausführung der Aktion sollte einen Bezeichner zum Auffinden der Ansicht zurückgeben. Am einfachsten wäre es, ihn als Dateinamen der JSP zu verwenden. Ordnen Sie dieses Servlet auf eine bestimmte url-pattern en web.xml z.B. /pages/* , *.do oder auch nur *.html .

Im Falle von Präfix-Mustern wie zum Beispiel /pages/* können Sie dann URLs aufrufen wie http://example.com/pages/register , http://example.com/pages/login usw. und bieten /WEB-INF/register.jsp , /WEB-INF/login.jsp mit den entsprechenden GET- und POST-Aktionen. Die Teile register , login usw. sind dann verfügbar über request.getPathInfo() wie im obigen Beispiel.

Bei der Verwendung von Suffix-Mustern wie *.do , *.html usw., dann können Sie URLs wie http://example.com/register.do , http://example.com/login.do usw., und Sie sollten die Codebeispiele in dieser Antwort ändern (auch die ActionFactory ) zum Extrahieren der register y login Teile von request.getServletPath() stattdessen.


Strategie-Muster

En Action sollte der Strategie-Muster . Er muss als abstrakter Typ/Schnittstellentyp definiert werden, der die Arbeit auf der Grundlage der Eingereicht Argumente der abstrakten Methode (dies ist der Unterschied zur Befehlsmuster wobei der Abstrakt-/Schnittstellentyp die Arbeit auf der Grundlage der Argumente erledigen sollte, die während der Erstellung der Umsetzung).

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

Vielleicht möchten Sie die Exception spezifischer mit einer benutzerdefinierten Ausnahme wie ActionException . Das ist nur ein einfaches Beispiel für einen Anstoß, der Rest liegt ganz bei Ihnen.

Hier ist ein Beispiel für eine LoginAction die (wie der Name schon sagt) den Benutzer anmeldet. Die User selbst ist wiederum ein Datenmodell . En Siehe ist sich der Anwesenheit 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.
        }
    }

}

Muster der Fabrikmethode

En ActionFactory sollte der Muster der Fabrikmethode . Grundsätzlich sollte sie eine Erstellungsmethode bereitstellen, die eine konkrete Implementierung eines abstrakten Typs/einer Schnittstelle zurückgibt. In diesem Fall sollte sie eine Implementierung des Typs Action Schnittstelle auf der Grundlage der von der Anfrage gelieferten Informationen. Zum Beispiel kann die Methode y Pfadinfo (die Pfadangabe ist der Teil nach dem Kontext und dem Servlet-Pfad in der Anfrage-URL, mit Ausnahme des Query-Strings).

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

En actions wiederum sollten einige statische/anwendungsweite Map<String, Action> die alle bekannten Aktionen enthält. Es liegt an dir, wie du diese Karte füllst. Hardcoding:

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

Oder konfigurierbar auf der Grundlage einer properties/XML-Konfigurationsdatei im Klassenpfad: (pseudo)

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

Oder dynamisch auf der Grundlage einer Suche im Klassenpfad nach Klassen, die eine bestimmte Schnittstelle und/oder Anmerkung implementieren: (Pseudo-)

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

Denken Sie daran, ein "Nichtstun" zu schaffen Action für den Fall, dass es kein Mapping gibt. Lassen Sie es zum Beispiel direkt die request.getPathInfo().substring(1) dann.


Andere Muster

Das waren bisher die wichtigsten Muster.

Um einen Schritt weiter zu gehen, könnten Sie die Muster der Fassade zur Erstellung einer Context Klasse, die wiederum die Anfrage- und Antwortobjekte umhüllt und mehrere bequeme Methoden anbietet, die an die Anfrage- und Antwortobjekte delegiert werden und diese als Argument an die Action#execute() Methode. Dadurch wird eine zusätzliche abstrakte Schicht hinzugefügt, um die rohe Servlet-API zu verbergen. Sie sollten dann grundsätzlich mit Null import javax.servlet.* Deklarationen in jedem Action Umsetzung. In Bezug auf JSF ist dies das, was die FacesContext y ExternalContext Klassen tun. Ein konkretes Beispiel finden Sie in diese Antwort .

Dann gibt es noch die Staatliches Muster für den Fall, dass Sie eine zusätzliche Abstraktionsschicht hinzufügen möchten, um die Aufgaben des Sammelns der Anforderungsparameter, ihrer Konvertierung, ihrer Validierung, der Aktualisierung der Modellwerte und der Ausführung der Aktionen zu teilen. In JSF-Begriffen ist dies das, was die LifeCycle tut.

Dann gibt es noch die Zusammengesetztes Muster für den Fall, dass Sie eine komponentenbasierte Ansicht erstellen möchten, die mit dem Modell verbunden werden kann und deren Verhalten vom Zustand des anforderungsbasierten Lebenszyklus abhängt. In JSF-Begriffen ist dies das, was die UIComponent vertreten.

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


Siehe auch:

13voto

Alexander Pogrebnyak Punkte 43705

Im ausgefeilten MVC-Muster ist das Servlet das "C" - der Controller.

Seine Hauptaufgabe ist die anfängliche Bewertung von Anfragen und die anschließende Weiterleitung der Verarbeitung auf der Grundlage der anfänglichen Bewertung an den jeweiligen Worker. Eine der Aufgaben des Workers kann darin bestehen, einige Beans der Präsentationsschicht einzurichten und die Anfrage an die JSP-Seite weiterzuleiten, um HTML zu rendern. Allein aus diesem Grund müssen Sie das Anforderungsobjekt an die Serviceschicht weitergeben.

Ich würde aber nicht anfangen, roh zu schreiben Servlet Klassen. Die Arbeit, die sie verrichten, ist sehr vorhersehbar und unausgegoren, etwas, das das Framework sehr gut kann. Glücklicherweise gibt es viele verfügbare, bewährte Kandidaten (in alphabetischer Reihenfolge): Apache Wicket , Java Server Faces , Frühling um nur einige zu nennen.

5voto

Kangkan Punkte 14327

IMHO gibt es im Falle von Webanwendungen keinen großen Unterschied, wenn man es aus dem Blickwinkel der Verantwortungszuweisung betrachtet. Behalten Sie jedoch die Übersichtlichkeit in der Ebene. Behalten Sie alles, was ausschließlich der Präsentation dient, in der Präsentationsschicht, wie z. B. die Steuerelemente und den für die Web-Steuerelemente spezifischen Code. Behalten Sie Ihre Entitäten in der Geschäftsschicht und alle Funktionen (wie Hinzufügen, Bearbeiten, Löschen) usw. in der Geschäftsschicht. Sie werden jedoch in der Präsentationsschicht auf den Browser übertragen und dort verarbeitet. Für .Net ist das ASP.NET MVC-Muster sehr gut geeignet, um die Schichten getrennt zu halten. Schauen Sie sich das MVC-Muster an.

3voto

EsotericNonsense Punkte 385

Ich habe die Streben Framework und finden es recht einfach zu erlernen. Bei Verwendung des Struts-Frameworks enthält jede Seite Ihrer Website die folgenden Elemente.

1) Eine Aktion, die verwendet wird, wird jedes Mal aufgerufen, wenn die HTML-Seite aufgefrischt wird. Die Aktion sollte die Daten im Formular auffüllen, wenn die Seite zum ersten Mal geladen wird, und die Interaktionen zwischen der Web-UI und der Geschäftsschicht verwalten. Wenn Sie die jsp-Seite verwenden, um ein veränderbares Java-Objekt zu ändern, sollte eine Kopie des Java-Objekts im Formular und nicht das Original gespeichert werden, damit die ursprünglichen Daten nicht geändert werden, bis der Benutzer die Seite speichert.

2) Das Formular, das für die Datenübertragung zwischen der Aktion und der JSP-Seite verwendet wird. Dieses Objekt sollte aus einer Reihe von Getter und Setter für Attribute bestehen, die für die JSP-Datei zugänglich sein müssen. Das Formular verfügt auch über eine Methode zur Validierung von Daten, bevor sie persistiert werden.

3) Eine jsp-Seite, die zum Rendern des endgültigen HTML-Satzes der Seite verwendet wird. Die jsp-Seite ist eine Mischung aus HTML und speziellen Struts-Tags für den Zugriff auf und die Bearbeitung von Daten im Formular. Obwohl Struts die Möglichkeit bietet, Java-Code in jsp-Dateien einzufügen, sollten Sie damit sehr vorsichtig sein, da Ihr Code dadurch schwieriger zu lesen ist. Java-Code in jsp-Dateien ist schwer zu debuggen und kann nicht als Unit getestet werden. Wenn Sie mehr als 4-5 Zeilen Java-Code in einer jsp-Datei schreiben, sollte der Code wahrscheinlich in die Action verschoben werden.

3voto

Ravindra babu Punkte 45577

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

Einige Anwendungen können Folgendes erfordern Verantwortungskette_Muster

Im objektorientierten Design ist die Verantwortungskette Muster ist ein Entwurfsmuster, das aus einer Quelle von Befehlsobjekten und einer Reihe von Verarbeitungsobjekten besteht. Jedes Verarbeitungsobjekt enthält eine Logik, die die Arten von Befehlsobjekten definiert, die es verarbeiten kann; der Rest wird an das nächste Verarbeitungsobjekt in der Kette weitergegeben.

Anwendungsfall zur Verwendung dieses Musters:

Wenn der Handler zur Bearbeitung einer Anfrage (Befehl) unbekannt ist und diese Anfrage an mehrere Objekte gesendet werden kann. Im Allgemeinen setzen Sie Nachfolger zu widersprechen. Wenn das aktuelle Objekt die Anfrage nicht oder nur teilweise bearbeiten kann und die gleiche Anfrage an Nachfolger Objekt.

Nützliche SE-Fragen/Artikel:

Warum sollte ich jemals eine Verantwortungskette anstelle eines Dekorateurs verwenden?

Häufige Verwendungen für die Verantwortungskette?

Verantwortungsketten-Muster von oodesign

Kette_der_Verantwortlichkeit aus der Quellenherstellung

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X