web-dev-qa-db-fra.com

Quelle est la spécificité du modèle de responsabilité unique pour les cours?

Par exemple, supposons que vous disposiez d'un programme de jeu de console, qui possède toutes sortes de méthodes d'entrée/sortie vers et depuis la console. Serait-il intelligent de les garder tous dans une seule classe inputOutput ou les casser sur des classes plus spécifiques telles que startMenuIO, inGameIO, playerIO, gameBoardIO, etc. de sorte que chaque classe a environ 1-5 méthodes?

Et sur la même note, s'il est préférable de les casser, serait-il intelligent de les placer dans un espace de noms IO rendant ainsi en les appelant un peu plus verbeux, E.g: IO.inGame etc.?

14
shinzou

Mise à jour (récapitulatif)

Depuis que j'ai écrit une réponse plutôt verbeuse, voici ce que tout se résume à:

  • Les espaces de noms sont bons, utilisez-les chaque fois que cela a du sens
  • Utiliser inGameIO et playerIO classes constitueraient probablement une violation du SRP. Cela signifie probablement que vous couplez la façon dont vous gérez IO avec la logique d'application.
  • Demandez à deux classes génériques IO utilisées (ou parfois partagées) par des classes de manutention. Ces classes de manutention traduiraient ensuite l'entrée brute en format que votre logique d'application peut donner un sens à.
  • Même chose pour la sortie: cela peut être fait par des classes assez génériques, mais passez l'état de jeu via un objet de gestionnaire/mappeur qui traduit l'état du jeu interne en quelque chose que les classes génériques IO peuvent gérer.

Je pense que vous regardez cela dans le mauvais sens. Vous séparez la fonction IO en fonction des composants de l'application, tandis que - à moi- il est plus logique d'avoir des classes distinctes IO basées sur la source et "type " de io.

Avoir des classes de base/générique KeyboardIOMouseIO pour commencer, puis sur la base de quand et où vous en avez besoin, avez des sous-classes qui manipulent IO différemment.
[.____] Par exemple, la saisie de texte est quelque chose que vous voulez probablement gérer différemment aux contrôles de jeu. Vous vous retrouverez à vouloir cartographier certaines clés différemment selon chaque cas d'utilisation, mais que la cartographie ne fait partie de IO elle-même, c'est comment vous gérez l'IO.

S'en tenir au SRP, j'aurais quelques classes que je peux utiliser pour le clavier IO. Selon la situation, je voudrais probablement interagir avec ces classes différemment, mais leur seul travail est de me dire ce que l'utilisateur fait.

J'injectirais ensuite ces objets dans un objet de gestionnaire qui ferait cartographier le brut IO sur quelque chose que ma logique d'application peut fonctionner avec (par exemple: appuis sur l'utilisateur "W", les cartes du gestionnaire que sur MOVE_FORWARD).

Ces gestionnaires, à leur tour, sont utilisés pour que les personnages se déplacent et dessinent l'écran en conséquence. Une simplification excessive brute, mais l'essentiel est ce genre de structure:

[ IO.Keyboard.InGame ] // generic, if SoC and SRP are strongly adhered to, changing this component should be fairly easy to do
   ||
   ==> [ Controls.Keyboard.InGameMapper ]

[ Game.Engine ] <- Controls.Keyboard.InGameMapper
                <- IO.Screen
                <- ... all sorts of stuff here
    InGameMapper.move() //returns MOVE_FORWARD or something
      ||
      ==> 1. Game.updateStuff();//do all the things you need to do to move the character in the given direction
          2. Game.Screen.SetState(GameState); //translate the game state (inverse handler)
          3. IO.Screen.draw();//generate actual output

Ce que nous avons maintenant est une classe qui est responsable du clavier IO dans sa forme brute. Une autre classe qui traduit ces données en quelque chose que le moteur de jeu peut réellement avoir du sens, ces données sont ensuite utilisées pour mettre à jour l'état de tous les composants impliqués et enfin, une classe distincte s'occupera de la sortie à l'écran.

Chaque classe dispose d'un seul travail: la manipulation de la saisie du clavier est effectuée par une classe qui ne sait pas/soins/doit savoir quelles sont les moyens de traitement d'entrée. Tout ce qu'il fait, c'est savoir comment Pour obtenir l'entrée (tamponnée, non volée, ...).

Le gestionnaire traduit cela dans une représentation interne pour le reste de l'application pour donner un sens à ces informations.

Le moteur de jeu prend les données traduites et l'utilise pour notifier à tous les composants pertinents que quelque chose se passe. Chacun de ces composants ne fait qu'une seule chose, que ce soit des contrôles de collision, ou des changements d'animation de caractères, cela n'a pas d'importance, c'est à chaque objet individuel.

Ces objets relisent ensuite leur état de l'état et ces données sont transmises à Game.Screen, Qui est essentiellement un gestionnaire inverse IO. Il mappe la représentation interne sur quelque chose que le composant IO.Screen Peut utiliser pour générer la sortie réelle.

7
Elias Van Ootegem

Le principe de responsabilité unique peut être difficile à comprendre. Ce que j'ai trouvé utile, c'est d'y penser comme comment vous écrivez des phrases. Vous n'essayez pas de cramer beaucoup d'idées en une seule phrase. Chaque phrase devrait indiquer une idée clairement et reporter les détails. Par exemple, si vous vouliez définir une voiture, vous diriez:

Un véhicule routier, typiquement à quatre roues, alimenté par un moteur à combustion interne.

Ensuite, vous définissez des choses comme "véhicule", "route", "roues", etc. séparément. Vous n'essaieriez pas de dire:

Un véhicule de transport de personnes sur une voie de circulation, une voie ou une voie sur des terres entre deux endroits qui a été pavé ou autrement amélioré pour permettre aux voyages qui comptent quatre objets circulaires qui tournent sur un essieu fixé sous le véhicule et sont alimentés par un moteur qui génère Puissance motrice par la combustion de l'essence, de l'huile ou d'un autre carburant avec de l'air.

De même, vous devriez essayer de rendre vos cours, méthodes, etc., indiquez le concept central aussi simplement que possible et de différer les détails à d'autres méthodes et classes. Tout comme avec des phrases d'écriture, il n'ya pas de règle difficile sur la taille de leur taille.

23
Vaughn Cato

Je dirais que la meilleure façon d'y aller est de les garder dans des classes séparées. Les petites classes ne sont pas mauvaises, en fait la plupart du temps, ils sont une bonne idée.

En ce qui concerne votre cas spécifique, je pense que le fait d'avoir la séparation peut vous aider à modifier la logique de l'un de ces gestionnaires spécifiques sans affecter les autres et, si nécessaire, il serait plus facile pour vous d'ajouter une nouvelle méthode d'entrée/sortie si elle est arrivée.

2
Zalomon