OO vs. Layered; Équilibrer la pureté OO & # 8221; pour faire avancer les choses

StackOverflow https://stackoverflow.com/questions/153394

  •  03-07-2019
  •  | 
  •  

Question

Je crois en OO, mais pas au point où des conceptions / implémentations inappropriées devraient être utilisées uniquement pour être "conformes à OO".

Comment gérer l'architecture en couches Serlvet / EJB / DataContainer:

  • Les servlets reçoivent les demandes et appellent une "couche de gestion". (par exemple, les EJB de session)
  • La couche de gestion localise les DataContainers à partir de la base de données et les manipule pour mettre en oeuvre la logique métier
  • Les DataContainers ne contiennent pas de code réel, il suffit d’obtenir / set correspondant à la base de données.

Cette approche est attrayante. les DataContainers savent clairement ce qu’ils font et il est très facile de savoir d’où proviennent les données.

En plus de ne pas être OO, cela conduit à des classes peu claires qui peuvent être difficiles à nommer et à organiser.

Même si nous essayions de vouloir être plus "OO" " (par exemple, en plaçant certaines de ces méthodes dans les DataConatiners), certaines de ces opérations fonctionnent sur plusieurs ensembles de données.

Comment empêchez-vous votre couche de gestion d'obtenir des procédures fastidieuses, sans toutefois polluer vos DataContainers avec la logique métier?

Exemple

class UserServlet {
  handleRequest() {
    String id = request.get("id");
    String name = request.get("name");
    if (UserBizLayer.updateUserName(id,name))
      response.setStatus(OK);
    else
      response.setStatus(BAD_REQUEST);
  }
}

class UseBizLayer {
    updateUserName(String id, String name) {
        long key = toLong(id);
        user = userDAO.find(key);
        if user == null
            return false;
        if (!validateUserName(name))
            return false;
        user.setName(name);
        userDAO.update(user);
        return true;
    }

    validateUserName(String name) {
        // do some validations and return
    }
}

class User {
    long key;
    String name;
    String email;

    // imagine getters/setters here
}
  • Nous ne voulons pas validateUserName sur l'utilisateur, car il ne fonctionne que sur un nom; Je suppose que cela pourrait entrer dans une autre classe, mais nous avons ensuite un autre processus "uti" procédural. classe de type
  • Nous ne voulons pas de méthodes de persistance sur l'utilisateur, car il est utile de découpler les structures de données de leur stratégie de persistance
  • Nous ne voulons pas de logique métier dans notre Servlet, car nous aurons peut-être besoin de réutiliser cette logique ailleurs
  • Nous ne voulons pas de notre logique métier dans notre utilisateur, car cela attire beaucoup trop la classe User, ce qui rend difficile la réutilisation de la logique métier et associe l'utilisateur à sa stratégie de persistance

Je réalise que cet exemple n’est pas si mal, mais imaginez 10 DataContainers et 20 BizLayer avec plusieurs méthodes chacune. Imaginez que certaines de ces opérations ne soient pas "centrées" sur un conteneur de données particulier.

Comment pouvons-nous empêcher que cela ne soit un désordre procédural?

Était-ce utile?

La solution

Je vais donc exposer mes réflexions à ce sujet dans quelques points:

  1. Il semble que, dans un système Java EE, il soit nécessaire de gérer la plomberie de Java EE. La plomberie ne tire pas toujours profit des concepts de OO, mais peut le faire avec un peu de créativité et de travail. Par exemple, vous pourriez tirer parti d'éléments tels que AbstractFactory, etc. pour vous aider à partager le plus possible cette infrastructure.
  2. Une grande partie de ce que vous cherchez est discutée dans l'excellent livre d'Eric Evans intitulé Domain Driven Design. Je vous recommande vivement de l'examiner car il aborde le problème de l'expression de la connaissance du domaine et de la gestion de l'infrastructure technique nécessaire à sa prise en charge.
  3. Après avoir lu et grossi une partie de DDD, je voudrais encapsuler mon infrastructure technique dans des référentiels. Les référentiels seraient tous écrits pour utiliser une stratégie de persistance basée sur vos EJB de session. Vous écririez une implémentation par défaut pour que sache comment parler à votre session EJBS. Pour que cela fonctionne, vous devez ajouter un peu de convention et spécifier cette convention / ce contrat dans vos interfaces. Les référentiels font tous les CRUD et ne devraient faire plus que cela si absolument nécessaires. Si vous disiez "Mes DAOS sont mes référentiels", je serais d'accord.
  4. Alors, continuez avec ça. Vous avez besoin de quelque chose pour encapsuler l'unité de travail qui est exprimée dans UseBizLayer. A ce niveau, je pense que la nature de cela est que vous êtes bloqué pour écrire du code qui sera tout un script de transaction. Vous créez une séparation de responsabilité et d'état. C'est généralement ce que j'ai vu dans les systèmes Java EE comme une sorte d'architecture par défaut. Mais ce n'est pas orienté objet. J'essaierais d'explorer le modèle et de voir si je pouvais au moins essayer de partager certains des comportements qui sont écrits dans les classes de classe.
  5. Une autre approche que j’ai utilisée auparavant consiste à supprimer les classes de BizLayer, puis à transférer les appels du domaine vers des serveurs vers les référentiels / DAO effectuant l’opération. Cependant, cela pourrait nécessiter des investissements dans la construction d'infrastructures. Mais vous pouvez faire beaucoup avec un framework tel que Spring et l'utilisation de certains concepts AOP pour que cela fonctionne bien et réduire la quantité d'infrastructure personnalisée nécessaire.

Autres conseils

puisque vous implémentez des classes et des objets, votre solution sera OO quelle que soit la manière dont vous superposez les éléments. Elle risque de ne pas être très bien structurée en fonction de votre situation / de vos besoins! ; -)

En ce qui concerne votre question spécifique, il serait dans certains cas logique que validateUserName appartienne à la classe User, chaque utilisateur souhaitant avoir un nom valide. Vous pouvez également avoir une classe d’utilitaire de validation en supposant que d’autres objets portent des noms qui utilisent les mêmes règles de validation. La même chose vaut pour le courrier électronique. Vous pouvez les scinder en classes NameValidator et EmailValidator, ce qui serait une bonne solution si elles étaient utilisées beaucoup. Vous pouvez également toujours fournir une fonction validateUserName sur l'objet utilisateur qui vient d'appeler la méthode de la classe d'utilitaire. Toutes ces solutions sont valables.

L’une des grandes joies à propos de OOD / OOP est que, lorsque la conception est correcte, vous savez que c’est correct, car beaucoup de choses tombent dans le modèle et vous permettent de le faire. vous ne pouviez pas le faire avant.

Dans ce cas, je créerais les classes NameValidator et EmailValidator, car il semble probable que d'autres entités auront des noms et des adresses électroniques à l'avenir, mais je fournirais les fonctions validateName et validateEmailAddress sur la classe User, ce qui en fait une interface plus pratique. pour les objets biz à utiliser.

le reste des puces "nous ne voulons pas" est correct; ils sont non seulement nécessaires pour la superposition correcte, mais ils sont également nécessaires pour une conception OO propre.

superposition et OO vont main dans la main sur la base d'une séparation des préoccupations entre les couches. Je pense que vous avez la bonne idée, mais vous aurez besoin de quelques classes d’utilitaires pour les validations courantes telles que présentées

Réfléchissez à la manière dont ces tâches seraient effectuées s'il n'y avait pas d'ordinateur et modélisez votre système de cette manière.

Exemple simple ... Le client remplit un formulaire pour demander un widget, le remet à un employé, ce dernier vérifie l'identité du client, traite le formulaire, obtient un widget, donne le widget et un enregistrement de la transaction au client et conserve un enregistrement de la transaction quelque part pour la société.

Le client stocke-t-il ses données? Non, l'employé le fait. Quel rôle joue l'employé lorsqu'il stocke les données du client? Client Records Keeper.

Le formulaire vérifie-t-il qu'il a été rempli correctement? Non, l'employé le fait. Quel rôle joue l'employé quand il le fait? Processeur de formulaire.

Qui donne le widget au client? L'employé agissant en tant que distributeur de widgets

Et ainsi de suite ...

Pour insérer ceci dans une implémentation Java EE ...

Le Servlet agit pour le compte du client en remplissant le formulaire (en extrayant les données de la requête HTTP et en créant l'objet Java approprié) et en le transmettant à l'employé approprié (EJB), qui effectue ensuite avec le formulaire les besoins être fait. Lors du traitement de la demande, il se peut que l’EJB ait besoin de la transmettre à un autre EJB spécialisé dans différentes tâches, notamment pour accéder à / stocker des informations depuis / vers le stockage (votre couche de données). La seule chose qui ne devrait pas mapper directement à l'analogie devrait être les détails sur la façon dont vos objets communiquent entre eux et sur la façon dont votre couche de données communique avec votre stockage.

J'ai moi-même eu les mêmes pensées.

Dans le MVC traditionnel, l’important est de séparer les parties Vue, Modèle et Contrôleur. Il semble judicieux de séparer le contrôleur et le modèle simplement parce que vous pouvez vous retrouver avec des objets modèles gonflés:


public class UserModel extends DatabaseModel implements XMLable, CRUDdy, Serializable, Fooable, Barloney, Baznatchy, Wibbling, Validating {
  // member fields
  // getters and setters
  // 100 interface methods
}

Alors que vous pouvez avoir des contrôleurs séparés (ou des modèles entiers) pour la plupart des interfaces ci-dessus, et oui, c'est plutôt de nature procédurale, mais j'imagine que c'est ainsi que les choses se passent aujourd'hui. Vous pouvez également vous rendre compte que certaines interfaces font la même chose (CRUDdy - stockage et extraction de base de données, Serializable - identique sous un format binaire, XMLable, identique sous XML). Vous devez donc créer un système unique pour gérer cela, avec chaque serveur potentiel étant une implémentation distincte gérée par le système. Dieu, c'est vraiment mal écrit.

Peut-être y a-t-il quelque chose comme "co-classes" Cela vous permet de disposer de fichiers sources distincts pour l'implémentation du contrôleur, qui agissent comme s'ils appartenaient à la classe de modèle sur laquelle ils agissent.

En ce qui concerne les règles de gestion, elles fonctionnent souvent sur plusieurs modèles en même temps et doivent donc être séparées.

Je pense que c'est une question de "séparation des préoccupations". Avec votre architecture en couches, vous semblez très loin de la bonne voie, mais vous devez peut-être en faire plus, c’est-à-dire créer des couches architecturales au sein de vos couches Java EE?

Un DataContainer ressemble beaucoup au modèle DTO (Data Transfer Objects).

Une conception OO moderne comporte de nombreuses petites classes, chacune liée à un petit nombre d'amis, p. ex. via composition. Cela pourrait produire beaucoup plus de classes et de plaques de chaudière Java que vous n'êtes vraiment à l'aise, mais cela devrait conduire à une conception qui est mieux stratifiée et plus facile à tester et à maintenir à l'unité.

(+ 1 pour la question, +1 pour la réponse indiquant si vous savez que vous avez le droit de superposer).

Crudely, le " Nous ne voulons pas " section doit aller. Soit vous voulez que ce soit juste, soit vous voulez que ça reste tel quel. Il est inutile de ne pas créer une classe lorsque vous en avez besoin. "Nous en avons beaucoup" est une mauvaise excuse.

En effet, il est mauvais d'exposer toutes les classes internes, mais d'après mon expérience, créer une classe par concept (par exemple, un utilisateur, un ID, un concept de base de données) ...) aide toujours.

À côté de cela, un motif de façade ne permet-il pas de résoudre l'existence de charges de classes BizRules, cachées derrière une interface bien organisée et propre?

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top