Est-il possible que la logique métier ne se glisse pas dans la vue?


31

J'ai développé plusieurs projets d'application Web au cours des 3 dernières années, à la fois personnels et professionnels, et je n'arrive pas à comprendre s'il est possible qu'au moins une logique métier ne se retrouve pas dans la couche d'affichage de l'application.

Dans la plupart des cas, il y aura des problèmes comme "Si l'utilisateur a sélectionné l'option x, l'application doit lui permettre de fournir des informations pour y, sinon il doit fournir des informations z". Ou effectuez une opération AJAX qui devrait appliquer certaines modifications au modèle mais PAS les valider jusqu'à ce que l'utilisateur l'ait explicitement demandé. Ce sont quelques-uns des problèmes les plus simples que j'ai rencontrés et je ne peux pas comprendre comment il est possible d'éviter une logique complexe dans la vue.

La plupart des livres que j'ai lus décrivant MVC présentent généralement des exemples très triviaux, comme les opérations CRUD qui mettent simplement à jour les données sur le serveur et les affichent, mais CRUD n'est pas le cas sur la plupart des applications riches.

Est-il possible d'obtenir une vue sans aucune logique métier?


2
Jetez un œil aux dérivations MVC MVP et MVVM (voir en.wikipedia.org/wiki/Model_View_Presenter et en.wikipedia.org/wiki/Model_View_ViewModel ), elles pourraient être ce que vous recherchez.
Doc Brown

liés (éventuellement un doublon): Découplage des classes de l'interface utilisateur
gnat

2
La vue est la manifestation externe et visible de vos données et de votre logique. Il n'est pas possible que la vue NE présente PAS de logique métier. Ou dites-vous que la vue ne doit pas contenir de code? Vous pouvez certainement créer des vues HTML uniquement.
BobDalgleish

Vous pourriez regarder dans l' animation de modèle ; bien que cela n'éliminera probablement pas toute la logique de la couche de vue , il semble que cela devrait conduire à une meilleure séparation des choses.
paul

Je pense qu'une meilleure question est de savoir s'il est préférable que les données de vue polluent le modèle ou est-il préférable que la vue contienne une logique de vue liée à la logique métier? C'est le scénario le plus réel. Votre question préconise essentiellement la pollution du modèle pour soutenir les opinions, car ce serait la seule façon d'accomplir ce que vous demandez.
Dunk

Réponses:


22

Est-il possible d'obtenir une vue sans aucune logique métier?

Je trouve que c'est une question trompeusement difficile à répondre. (Question qui fait réfléchir!)

Théoriquement, oui, selon ce que nous définissons comme logique métier. En pratique, une séparation stricte devient beaucoup plus difficile, et peut-être même indésirable.

La séparation des préoccupations est un excellent moyen de penser à la création de logiciels: elle vous donne des idées sur l'endroit où placer le code et elle donne aux responsables une bonne idée de l'endroit où chercher le code. Je dirai qu'il est fondamentalement impossible pour les humains de créer des logiciels qui fonctionnent sans séparation des préoccupations. Nous en avons besoin.

Mais, comme pour tout, il y a des compromis. Le meilleur emplacement conceptuel peut ne pas être le meilleur emplacement pour d'autres raisons. Peut-être qu'il y a trop de charge sur votre serveur Web, vous ajoutez donc du javascript à vos pages Web pour détecter les erreurs de saisie faciles avant qu'elles n'atteignent votre serveur; vous avez maintenant une certaine logique métier à votre avis.

La vue elle-même, à elle seule, n'a aucune valeur sans la logique métier. Et pour être efficace dans l'utilisation et l'affichage, implicitement ou explicitement, la vue aura une certaine connaissance des processus métier en cours. Nous pouvons limiter cette quantité de connaissances, et nous pouvons en boucler certaines parties, mais des considérations pratiques nous obligent souvent à «rompre» la séparation des préoccupations.


2
The best conceptual location may not be the best location for other reasons: Bravo !!
Magno C

8

Je fais généralement ceci: si l'utilisateur a sélectionné l'option x, la vue appelle

controller->OptionXChanged()

Ensuite, le contrôleur active y sur la vue:

view->SetEnableInfoY(True) // suppose False=SetDisable

La vue informe le contrôleur de ce qui se passe sans rien décider.


+1. Les problèmes triviaux d'OP sont généralement traités comme ceci dans une application non triviale
dev_feed

La mise en place de cette logique dans le contrôleur présente deux problèmes: 1) elle complique les tests unitaires et il est impossible de prendre en charge plusieurs vues des mêmes données.
kevin cline

4

Je me demande si les exemples que vous décrivez sont vraiment de la logique métier. Les exemples que vous décrivez sont des opérations qui peuvent être effectuées sur le système. C'est la façon dont vous avez choisi de présenter les choix à l'utilisateur qui donne peut-être l'impression que vous faites de la logique métier dans la vue.

Du point de vue "View", il fournit uniquement InfoY ou InfoZ au système. Ce n'est pas parce que votre implémentation d'interface utilisateur effectue des mises à jour dynamiques basées sur un choix d'opérateur (c'est-à-dire l'activation d'InfoY ou d'InfoZ) que la fonctionnalité est logique. C'est vraiment une logique d'implémentation de vue. Vous auriez très bien pu simplement donner à l'opérateur le choix d'entrer InfoY ou InfoZ sans l'activation complète. Dans ce contexte, considéreriez-vous toujours cela comme une logique métier? Sinon, la même chose s'applique pour l'activation / désactivation dynamique des champs d'informations.

Il en va de même pour l'exemple de validation. Il s'agit de 2 opérations distinctes dont le système a besoin pour fonctionner correctement. Votre vue doit être en mesure de lancer les actions appropriées pour exécuter la fonctionnalité souhaitée. Le fait de savoir comment utiliser votre système signifie-t-il une fuite de la logique métier? Je peux voir comment quelqu'un pourrait dire oui, mais si vous croyez de cette façon, la réalité est qu'il n'y a rien de tel que la séparation de la logique métier de quoi que ce soit. Vous devez savoir ce que le système fait / travaille pour accomplir quelque chose de significatif. Sinon, ce serait un jeu d'enfant de créer une seule vue et contrôleur générique qui fonctionne avec toutes les applications MVC imaginables. Ce que nous savons est impossible.

En bout de ligne, je pense que votre définition de la logique métier n'est pas la même que celle des autres.


1

Je travaille de cette façon (Struts2 + Hibernate):

My Struts Actions est uniquement responsable de l'affichage des informations sur le navigateur Web. Ne pas penser.

Utilisateur -> Action -> Service -> Référentiel -> Accès aux données

Ou:

Je veux voir -> Comment voir -> Que faire -> Comment obtenir -> Où trouver

Donc, dans la première couche (la vue), j'ai quelque chose comme:

public String execute ()   {
    try {
        CourseService cs = new CourseService();
        Course course = cs.getCourse(idCourse);
    } catch (NotFoundException e) {
        setMessageText("Course not found.");
    } catch (Exception e) {

    }
    return "ok";
}

Comme vous le voyez, mon "point de vue" ne pense pas. C'est demander un service (pour gérer des cours) un cours spécifique. Ce service peut faire beaucoup plus de choses, comme des rapports, des seraches, etc. Le résultat est toujours une liste ou un objet spécifique (comme l'exemple). Les services sont la vraie machine, appliquent des règles et accèdent au référentiel (pour gérer les données).

Donc, si je place mes services, référentiels et DAOS dans différentes bibliothèques, je peux les utiliser même dans un programme texte ou un système de bureau Windows sans rien changer.

Le service sait quoi faire, mais ne sait pas montrer. La vue sait montrer, mais ne sait pas quoi faire. La même chose avec Service / Repository: Le service envoie et demande les données, mais ne sait pas où se trouvent les données et comment les prendre. Le référentiel "compose" les données brutes des objets de cuisine afin que le Service puisse travailler avec.

Mais le référentiel ne sait rien de la base de données. Le type de base de données (MySQL, PostgreSQL, ...) concerne DAO.

Vous pouvez modifier le DAO si vous souhaitez modifier la base de données et cela ne doit pas affecter les couches supérieures. Vous pouvez modifier le référentiel si vous souhaitez mettre à jour votre gestion des données, mais cela ne doit pas affecter le DAO et les couches supérieures. Vous pouvez changer les Services si vous voulez changer votre logique, mais cela ne doit pas gâcher les couches au-dessus ou en dessous.

Et vous pouvez changer quoi que ce soit en vue, même la technologie (web, bureau, texte) mais cela ne doit pas impliquer de toucher quoi que ce soit ci-dessous.

La logique métier est le service. Mais comment interagir avec cela, c'est voir. Quel bouton afficher maintenant? L'utilisateur peut-il voir ce lien? Vous pensez que votre système est un programme basé sur une console: vous devez refuser si le mauvais utilisateur choisit #> myprogram -CourseService -option=getCourse -idCourse=234ou l'arrête d'appuyer sur les touches pour écrire cette commande?

Parler dans des systèmes basés sur le Web (Struts + JavaEE) J'ai un package de contrôleur GUI séparé. En vue Action, je donne à l'utilisateur connecté et la classe me donne les boutons (ou tout élément d'interface que je veux).

                <div id="userDetailSubBox">
                    <c:forEach var="actionButton" items="${actionButtons}" varStatus="id">
                        ${actionButton.buttonCode}
                    </c:forEach>
                </div>

Et

private List<ActionButton> actionButtons;

N'oubliez pas de garder cela hors des services. Ce sont des trucs VIEW. Gardez-le dans les actions Struts. Toutes les interactions d'interface doivent être entièrement séparées du vrai code d'entreprise, donc si vous portez votre système, il sera facile de couper ce dont vous n'aurez plus besoin.


1

Dans la plupart des cas, il y aura des problèmes comme "Si l'utilisateur a sélectionné l'option x, l'application doit lui permettre de fournir des informations pour y, sinon il doit fournir des informations z"

C'est logique pour le modèle, pas pour la vue. Il peut s'agir d'un "modèle de vue", créé spécifiquement pour prendre en charge l'interface utilisateur, mais il s'agit toujours d'une logique de modèle. La séquence de contrôle est la suivante:

  • Le contrôleur attache un gestionnaire pour afficher les événements
  • View attache un gestionnaire pour les événements de modèle
  • L'utilisateur sélectionne l'option X.
  • La vue déclenche un événement "Option X sélectionnée"
  • Le contrôleur reçoit l'événement et appelle model.selectOptionX ()
  • Le modèle déclenche un événement "L'état du modèle a changé"
  • La vue reçoit l'événement de modèle modifié et met à jour la vue pour correspondre au nouvel état: inputY.enable(model.yAllowed()); inputZ.enable(model.zAllowed());

UI View Controller Model |.checkbox X checked.> | | | | | .. X selected ...>| | | | |-----> set X ------->| | | | | | |< .............state changed ............| | | | | | |-------------- Get state --------------->| | | | | | |<----------- new state ------------------| | <-- UI updates ------| Il s'agit du modèle MVC classique. Il est possible de tester complètement la logique du modèle indépendamment de l'interface utilisateur. Le contrôleur et la vue sont très fins et faciles à tester.

=== En réponse à Dunk ===

Le modèle dans un modèle d'interface utilisateur MVC n'est (généralement) pas un modèle d'objet métier. Ce n'est que le modèle de l'état de l'interface utilisateur. Dans une application de bureau, il peut contenir des références à plusieurs modèles commerciaux. Dans une application Web 2.0, il s'agit d'une classe Javascript qui détient l'état de l'interface utilisateur et communique via AJAX au serveur. Il est très important de pouvoir écrire des tests unitaires interactifs du modèle d'état de l'interface utilisateur, car c'est là que se trouvent la plupart des bogues de l'interface utilisateur. La vue et le contrôleur doivent être des connecteurs très fins.


1
Je suppose que tout se résume à ce que vous croyez être la définition de MVC. Cette version adhère définitivement à une interprétation très, très stricte de MVC. Le problème est que cette interprétation stricte fournit rarement un système utile ou maintenable dans la vie réelle. La raison étant qu'à peu près à chaque fois que vous proposez un nouvel élément d'interface utilisateur / une nouvelle façon de faire quelque chose, vous devez changer le modèle. Le modèle devient alors encombré de propriétés inutiles uniquement pertinentes pour l'interface utilisateur. Ceux-ci n'ont rien à voir avec l'application que vous essayez de créer, mais uniquement avec la façon dont vous souhaitez présenter les données à l'opérateur. MAUVAIS!
Dunk

kevin veuillez mettre vos réponses ici dans la boîte de commentaires, il nous est donc facile de vous répondre. Je suis d'accord avec toi. Il n'est pas possible de conserver les informations d'interface (UI) sans aucun type de structure, mais la nomenclature "MODEL" peut prêter à confusion. Je préfère gérer les choses de l'interface utilisateur dans un package interchangeable différent pour être facile à faire de ce dont parle @Dunk. Voir ma réponse.
Magno C

@MagnoC: J'ai modifié sa réponse en réponse à Dunk parce que je pensais que le texte ajouté améliorait la réponse. C'est à cela que sert le site: questions et réponses. Le modèle est un terme assez général, et dans le modèle MVC, il signifie "modèle d'état de l'interface utilisateur".
kevin cline

0

Une logique métier ressemble plus à cela If X then return InfoType.Y, puis l'interface utilisateur affichera des champs en fonction du résultat renvoyé par le domaine.

// Controller method pseudocode
option changed routine

    get selected option

    get required info type from domain routine based on selected option

    display fields based on required info type

Si l'interface utilisateur nécessite une logique métier, déléguez le choix au domaine. L'interface utilisateur agira simplement sur la décision.


0

Si l'utilisateur a sélectionné l'option x, l'application doit lui permettre de fournir des informations pour y, sinon il doit fournir des informations z ".

Il existe des entrées qui ont des valeurs requises conditionnelles. Dans la plupart des environnements GUI, il existe de nombreux choix sur la façon de gérer les entrées, en particulier le timing. L'option sélectionnée (dans ce cas x) doit être traitée, alors envoyez-la au contrôleur. Envoyez-le lorsque les utilisateurs quittent le champ de saisie. Attendez qu'ils cliquent sur un autre objet ou appuyez sur enregistrer. Peu importe pour la logique métier. D'une manière ou d'une autre, le contrôleur prendra une décision et devra dire à la vue "y est requis".

La façon dont la vue interprète ou implémente cela n'a pas vraiment d'importance du point de vue de la logique métier. Faites votre champ obligatoire. Ayez une fenêtre contextuelle ou lancez un canon et dites à l'utilisateur d'entrer y ou tout simplement d'être têtu et de ne pas laisser le pauvre utilisateur faire quoi que ce soit avant d'avoir compris cela.

Et pensez-y, tout cela s'est peut-être produit parce que le contrôleur a tenté d'enregistrer et n'a pas entré de valeur pour un champ obligatoire dans la base de données et répondait uniquement à une erreur de base de données. Peu importe la vue.

Quelque chose comme une valeur requise ou limitée pour une entrée peut être gérée à de nombreux endroits. Si vous ne le résolvez "que" dans la vue, de nombreux développeurs voient cela comme un problème lorsqu'il peut y avoir plusieurs interfaces utilisateur. C'est pourquoi la logique métier peut être créée et testée sans beaucoup d'interface utilisateur ni même de base de données. Vous n'avez même pas besoin d'avoir un site Web.

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.