Design Patterns applications basées sur le Web [fermé]


359

Je conçois une application Web simple. Je suis nouveau dans ce domaine basé sur le Web, j'avais besoin de vos conseils concernant les modèles de conception tels que la façon dont la responsabilité devrait être répartie entre les servlets, les critères de création de nouveaux servlets, etc.

En fait, j'ai peu d'entités sur ma page d'accueil et correspondant à chacune d'entre elles, nous avons peu d'options comme ajouter, modifier et supprimer. Auparavant, j'utilisais un Servlet par options comme Servlet1 pour ajouter entité1, Servlet2 pour modifier entité1 et ainsi de suite et de cette façon, nous avons fini par avoir un grand nombre de servlets.

Maintenant, nous changeons notre conception. Ma question est de savoir comment vous choisissez exactement comment vous choisissez la responsabilité d'une servlet. Devrions-nous avoir un servlet par entité qui traitera toutes ses options et transmettra la demande à la couche service. Ou devrions-nous avoir un servlet pour la page entière qui traitera la demande de page entière et la transmettra ensuite à la couche de service correspondante? En outre, si l'objet de demande est transmis à la couche de service ou non.


8
Pas vraiment des modèles de conception officiels, mais n'oubliez pas PRG (post-redirect-get) et Hijax (faites d'abord fonctionner sans js, puis détournez les liens et les boutons avec ajax)
Neil McGuigan

Réponses:


489

Une application Web un peu décente consiste en un mélange de modèles de conception. Je ne mentionnerai que les plus importants.


Modèle de contrôleur de vue du modèle

Le modèle de conception de base (architectural) que vous souhaitez utiliser est le modèle Model-View-Controller . Le contrôleur doit être représenté par un servlet qui (en) crée / utilise directement un modèle et une vue spécifiques en fonction de la demande. Le modèle doit être représenté par des classes javabéennes. Ceci est souvent divisible en Business Model qui contient les actions (comportement) et Data Model qui contient les données (information). La vue doit être représentée par des fichiers JSP qui ont un accès direct au modèle (de données ) par EL (langage d'expression).

Ensuite, il existe des variations basées sur la façon dont les actions et les événements sont traités. Les plus populaires sont:

  • MVC basé sur demande (action) : c'est le plus simple à implémenter. Le modèle ( commercial ) fonctionne directement avec et les objets. Vous devez collecter, convertir et valider (principalement) les paramètres de demande vous-même. La vue peut être représentée par du HTML / CSS / JS simple et vanille et elle ne maintient pas l'état d'une requête à l'autre. C'est ainsi que fonctionne entre autres Spring MVC , Struts and Stripes .HttpServletRequestHttpServletResponse

  • MVC basé sur les composants : c'est plus difficile à mettre en œuvre. Mais vous vous retrouvez avec un modèle et une vue plus simples dans lesquels toute l'API Servlet "brute" est complètement abstraite. Vous ne devriez pas avoir à rassembler, convertir et valider les paramètres de demande vous-même. Le contrôleur effectue cette tâche et définit les paramètres de demande collectés, convertis et validés dans le modèle . Tout ce que vous devez faire est de définir des méthodes d'action qui fonctionnent directement avec les propriétés du modèle. La vue est représentée par des "composants" de type balises JSP ou éléments XML qui à leur tour génèrent du HTML / CSS / JS. L'état de la vuepour les demandes ultérieures est conservée dans la session. Cela est particulièrement utile pour les événements de conversion, de validation et de changement de valeur côté serveur. C'est ainsi qu'entre autres JSF , Wicket et Play! travaux.

En guise de remarque, passer du temps avec un framework MVC local est un très bon exercice d'apprentissage, et je le recommande aussi longtemps que vous le gardez à des fins personnelles / privées. Mais une fois que vous êtes devenu professionnel, il est fortement recommandé de choisir un cadre existant plutôt que de réinventer le vôtre. Apprendre un cadre existant et bien développé prend à long terme moins de temps que de développer et de maintenir vous-même un cadre robuste.

Dans l'explication détaillée ci-dessous, je me limiterai à demander un MVC basé car c'est plus facile à implémenter.


Modèle de contrôleur avant ( modèle de médiateur )

Tout d'abord, la partie contrôleur doit implémenter le modèle de contrôleur avant (qui est un type spécialisé de modèle de médiateur ). Il ne doit être composé que d'un seul servlet qui fournit un point d'entrée centralisé de toutes les demandes. Il doit créer le modèle sur la base des informations disponibles par la demande, telles que pathinfo ou servletpath, la méthode et / ou des paramètres spécifiques. Le modèle d'entreprise est appelé Actiondans l' HttpServletexemple ci-dessous .

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

L'exécution de l'action doit renvoyer un identifiant pour localiser la vue. Le plus simple serait de l'utiliser comme nom de fichier du JSP. Mappez cette servlet sur un url-patterndans spécifique web.xml, par exemple /pages/*, *.doou même juste *.html.

Dans le cas de modèles de préfixe comme par exemple, /pages/*vous pouvez alors invoquer des URL comme http://example.com/pages/register , http://example.com/pages/login , etc. et fournir /WEB-INF/register.jsp, /WEB-INF/login.jspavec les actions GET et POST appropriées . Les parties register, loginetc sont alors disponibles par request.getPathInfo()comme dans l' exemple ci - dessus.

Lorsque vous utilisez suffixe-modèles comme *.do, *.html, etc, alors vous pouvez maintenant appeler URL comme http://example.com/register.do , http://example.com/login.do , etc et vous devez changer la des exemples de code dans cette réponse (également le ActionFactory) pour extraire les parties registeret loginpar request.getServletPath().


Modèle de stratégie

Ils Actiondevraient suivre le modèle de stratégie . Il doit être défini comme un type abstrait / interface qui devrait faire le travail sur la base des arguments transmis de la méthode abstraite (c'est la différence avec le modèle de commande , dans lequel le type abstrait / interface devrait faire le travail basé sur le arguments transmis lors de la création de l'implémentation).

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

Vous voudrez peut-être rendre le Exceptionplus spécifique avec une exception personnalisée comme ActionException. C'est juste un exemple de coup d'envoi de base, le reste dépend de vous.

Voici un exemple d'un LoginActionqui (comme son nom l'indique) connecte l'utilisateur. Le Userlui-même est à son tour un modèle de données . La Vue est consciente de la présence du 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.
        }
    }

}

Modèle de méthode d'usine

Ils ActionFactorydoivent suivre le modèle de méthode Factory . Fondamentalement, il devrait fournir une méthode de création qui renvoie une implémentation concrète d'un type abstrait / interface. Dans ce cas, il doit renvoyer une implémentation de l' Actioninterface basée sur les informations fournies par la demande. Par exemple, la méthode et pathinfo (le pathinfo est la partie après le contexte et le chemin du servlet dans l'URL de la requête, à l'exclusion de la chaîne de requête).

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

À actionsson tour, il doit s'agir d'une partie statique / à l'échelle de l'application Map<String, Action>contenant toutes les actions connues. C'est à vous de savoir comment remplir cette carte. Codage dur:

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

Ou configurable sur la base d'un fichier de configuration propriétés / XML dans le chemin de classe: (pseudo)

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

Ou dynamiquement basé sur un scan dans le chemin de classe pour les classes implémentant une certaine interface et / ou annotation: (pseudo)

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

N'oubliez pas de créer un "ne rien faire" Actionpour le cas où il n'y a pas de mappage. Laissez-le par exemple retourner directement le request.getPathInfo().substring(1)then.


Autres motifs

Tels étaient les schémas importants jusqu'à présent.

Pour aller plus loin, vous pouvez utiliser le modèle Facade pour créer une Contextclasse qui, à son tour, encapsule les objets de demande et de réponse et propose plusieurs méthodes pratiques de délégation aux objets de demande et de réponse et les transmet à la Action#execute()place comme argument dans la méthode. Cela ajoute une couche abstraite supplémentaire pour masquer l'API Servlet brute. Vous devriez alors vous retrouver avec zéro import javax.servlet.* déclaration dans chaque Actionimplémentation. En termes JSF, c'est ce que font les classes FacesContextet ExternalContext. Vous pouvez trouver un exemple concret dans cette réponse .

Ensuite, il y a le modèle d'état pour le cas où vous souhaitez ajouter une couche d'abstraction supplémentaire pour diviser les tâches de collecte des paramètres de demande, de les convertir, de les valider, de mettre à jour les valeurs du modèle et d'exécuter les actions. En termes JSF, c'est ce que le LifeCyclefait.

Ensuite, il y a le modèle composite pour le cas dans lequel vous souhaitez créer une vue basée sur les composants qui peut être attachée au modèle et dont le comportement dépend de l'état du cycle de vie basé sur la demande. En termes JSF, c'est ce que UIComponentreprésentent.

De cette façon, vous pouvez évoluer petit à petit vers un framework basé sur des composants.


Voir également:


4
@masato: Vous pouvez le faire par exemple dans un bloc d'initialisation statique.
BalusC

1
@masato: soit dit en passant, si vous souhaitez les récupérer web.xml, vous pouvez utiliser un ServletContextListenerpour cela. Demandez à l'usine de l'implémenter (et d'enregistrer comme <listener>dans web.xml) et de faire le travail de remplissage pendant la contextInitialized()méthode.
BalusC

3
Faites le travail que le "post_servlet" devrait faire dans l'action à la place. Vous ne devriez pas avoir plus d'une servlet. Les affaires devraient être faites dans des classes d'action. Si vous souhaitez qu'il s'agisse d'une nouvelle demande, revenez à une autre vue qui entraînerait une redirection et ferait le travail dans la nouvelle action associée à la demande GET.
BalusC

2
Dépend. Le plus simple est de le faire correctement dans l' Actionimplémentation de la même manière qu'avec les servlets normaux (voir aussi le wiki des servlets pour un exemple de base, que vous êtes libre de refactoriser plus loin dans une Validatorinterface). Mais vous pouvez également le faire avant d'appeler l'action, mais c'est plus complexe car cela nécessite que les règles de validation soient connues pour chaque vue. JSF a couvert en offrant required="true", validator="customValidatorName", etc dans le balisage XHTML.
BalusC

2
@AndreyBotalov: vérifiez le code source des frameworks MVC comme JSF, Spring MVC, Wicket, Struts2, etc. Ils sont tous open source.
BalusC

13

Dans le modèle MVC battu, le Servlet est un contrôleur "C".

Son travail principal consiste à effectuer une évaluation initiale de la demande, puis à envoyer le traitement basé sur l'évaluation initiale au travailleur spécifique. L'une des responsabilités du travailleur peut être de configurer certains beans de couche de présentation et de transmettre la demande à la page JSP pour rendre le HTML. Ainsi, pour cette seule raison, vous devez passer l'objet de demande à la couche service.

Cependant, je ne commencerais pas à écrire des Servletclasses brutes . Le travail qu'ils font est très prévisible et passe-partout, ce que le cadre fait très bien. Heureusement, il existe de nombreux candidats disponibles et testés dans le temps (dans l'ordre alphabétique): Apache Wicket , Java Server Faces , Spring pour n'en nommer que quelques-uns.


5

À mon humble avis, il n'y a pas beaucoup de différence en cas d'application web si vous la regardez sous l'angle de l'attribution des responsabilités. Cependant, conservez la clarté du calque. Conservez tout ce qui est purement à des fins de présentation dans la couche de présentation, comme le contrôle et le code spécifiques aux contrôles Web. Gardez simplement vos entités dans la couche métier et toutes les fonctionnalités (comme ajouter, modifier, supprimer), etc. dans la couche métier. Cependant, les rendre sur le navigateur à gérer dans la couche de présentation. Pour .Net, le modèle ASP.NET MVC est très bon en termes de séparation des couches. Examinez le modèle MVC.


pouvez-vous aller un peu explicite dans ce qui devrait aller dans le servlet?
mawia

Le servlet doit être le contrôleur si vous utilisez MVC.
Kangkan

3

J'ai utilisé le cadre Struts et je le trouve assez facile à apprendre. Lorsque vous utilisez le framework struts, chaque page de votre site comporte les éléments suivants.

1) Une action utilisée est appelée à chaque actualisation de la page HTML. L'action doit remplir les données du formulaire lors du premier chargement de la page et gérer les interactions entre l'interface utilisateur Web et la couche de gestion. Si vous utilisez la page jsp pour modifier un objet java mutable, une copie de l'objet java doit être stockée dans le formulaire plutôt que l'original afin que les données d'origine ne soient pas modifiées sauf si l'utilisateur enregistre la page.

2) Le formulaire utilisé pour transférer les données entre l'action et la page jsp. Cet objet doit consister en un ensemble de getter et de setters pour les attributs qui doivent être accessibles au fichier jsp. Le formulaire dispose également d'une méthode pour valider les données avant qu'elles ne persistent.

3) Une page jsp qui est utilisée pour rendre le HTML final de la page. La page jsp est un hybride de balises HTML et de balises spéciales utilisées pour accéder et manipuler les données du formulaire. Bien que struts permette aux utilisateurs d'insérer du code Java dans des fichiers jsp, vous devez être très prudent à ce sujet car cela rend votre code plus difficile à lire. Le code Java dans les fichiers jsp est difficile à déboguer et ne peut pas être testé à l'unité. Si vous vous retrouvez en train d'écrire plus de 4-5 lignes de code java dans un fichier jsp, le code devrait probablement être déplacé vers l'action.


Remarque: dans les struts 2, l'objet Form est appelé un modèle à la place, mais fonctionne de la même manière que je l'ai décrit dans ma réponse d'origine.
EsotericNonsense

3

L' excellente réponse de BalusC couvre la plupart des modèles d'applications Web.

Certaines applications peuvent nécessiter Chaîne de responsabilité_motif

Dans la conception orientée objet, le modèle de chaîne de responsabilité est un modèle de conception composé d'une source d'objets de commande et d'une série d'objets de traitement. Chaque objet de traitement contient une logique qui définit les types d'objets de commande qu'il peut gérer; le reste est transmis à l'objet de traitement suivant de la chaîne.

Cas d'utilisation pour utiliser ce modèle:

Le gestionnaire de traitement d'une demande (commande) est inconnu et cette demande peut être envoyée à plusieurs objets. Généralement, vous définissez successeur sur object. Si l'objet actuel ne peut pas traiter la demande ou la traiter partiellement et transmettre la même demande à l' objet successeur .

Questions / articles SE utiles:

Pourquoi devrais-je jamais utiliser une chaîne de responsabilité sur un décorateur?

Usages courants de la chaîne de responsabilité?

modèle de chaîne de responsabilité de oodesign

chain_of_responsibility de sourcemaking

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.