web-dev-qa-db-fra.com

Est-ce que cela viole le principe de responsabilité unique si un objet sait se changer?

J'ai travaillé avec des exemples comme un objet Player qui sait changer son propre état.

Un autre exemple est un objet Facture qui savait calculer ses frais de facturation à l'aide d'un algorithme.

Cela viole-t-il le principe de responsabilité unique?

Si oui, la création d'un InvoiceCalculator est-elle la bonne approche pour héberger l'algorithme?

Et le PlayerStateChanger est une chose valide.

8
GaVaRaVa

Je suis arrivé à la conclusion que le "principe de responsabilité unique" fait plus de mal que de bien en raison de son nom déroutant.

Le principe dit pas qu'une classe ne devrait être autorisée qu'à faire une seule chose spécifique. Si tel était le cas, ce serait un principe vraiment stupide!

Un objet sachant comment changer son état est parfaitement bien - en effet, beaucoup diront que seulement l'objet lui-même devrait pouvoir changer son état. Un objet de facture capable de calculer les frais de facturation semble également très bien.

Le PRS consiste à séparer les préoccupations qui pourraient changer indépendamment. Un objet de facture qui contient une logique de rendu comme PDF et logique de calcul des frais enfreindrait le principe, car l'apparence et la présentation de la facture peuvent changer indépendamment de l'algorithme de calcul des frais.

(Notez que "changer" fait référence ici à des changements dans les exigences qui obligent les développeurs à modifier le code, pas aux changements d'état d'exécution qui est un problème distinct.)

Le SRP peut nous conduire à définir de nombreuses petites classes juste pour être sûr de la séparation. Mais le principe doit être mis en balance avec son homologue, que les choses qui changent à l'unisson devraient être couplé dans le code. C'est ce qu'on appelle un "couplage faible mais une cohésion élevée".

20
JacquesB

Je déconseille de concevoir des objets de données (objets de domaine) qui peuvent changer eux-mêmes.

Je préfère faire la distinction suivante:

  1. Objets de domaine - Objets qui transportent des données, par exemple Player, Invoice. De préférence immuable, mais dépend de l'application (par exemple, pour un jeu, vous devrez peut-être avoir des objets mutables pour plus d'efficacité)
  2. Objets contenant une logique métier: InvoiceCalculator ou Bank objet qui dépose de l'argent dans Accounts

Avantages:

  • Vous pouvez passer des objets de domaine à différents threads sans craindre les conditions de concurrence ni la nécessité d'une synchronisation des threads, car ils sont immuables
  • Vous pouvez sérialiser des objets de domaine plus facilement
  • Vous pouvez les placer dans des ensembles et dans des cartes sous forme de clés (si vous implémentez equals/hashCode). Parce qu'ils sont immuables, vous pouvez le faire en toute sécurité.
  • En ayant une logique similaire dans une classe affaires, par exemple Classe bancaire qui retire/dépose de l'argent sur un compte:
    • Vous pouvez facilement trouver cette fonctionnalité lorsque vous la recherchez
    • Vous pouvez mieux contrôler l'accès à partir de différents threads

Ce que je décris ci-dessus a été qualifié de modèle de domaine anémique , par Martin Fowler. Je préfère l'appeler séparation des préoccupations .

N'oubliez pas que la programmation n'est pas un dogme, alors ne vous découragez pas pour ceux qui pourraient dire "ce n'est pas vrai OOP". OOP n'est qu'un paradigme - il y a aussi les procédures et les fonctions.

Lorsque l'article a été rédigé en 2003, les programmes multicœurs n'étaient pas aussi courants qu'aujourd'hui. De nos jours, ils le sont, donc j'aime garder mes objets de données simples et immuables et gérer la logique ailleurs.

3
Minas Mina

Un logiciel a de nombreuses parties prenantes et chacune de ces parties prenantes peut demander des modifications.

Par exemple, une application de facturation a plusieurs parties prenantes:

  • L'administrateur de la base de données qui est responsable de la façon dont les données de facturation sont stockées
  • Le directeur des opérations (COO) qui donne les exigences légales sur la façon dont chaque article est taxé et comment les remises sont calculées
  • Le comptable qui décide comment les données doivent être présentées dans le rapport de facturation imprimé

Le principe de responsabilité unique a pour but de vous aider à freiner quelque chose de la partie prenante B lorsque vous effectuez un changement demandé par la partie prenante A. La responsabilité unique signifie que chaque classe doit être responsable devant une seule personne (ou mieux, un rôle) dans l'entreprise.

La dernière chose que vous voulez faire, c'est que le COO vous crie pour casser le calcul de la taxe, tout en migrant vers un autre schéma de base de données ou en modifiant la présentation du rapport que le comptable vous a demandé.

Maintenant, spécifiquement pour le cas de la facture dans votre exemple, le modèle BCE vous recommande de faire une distinction entre la logique indépendante de l'application et la logique spécifique à l'application. Une facture est un objet métier (alias domaine). Cet objet pourrait aussi bien être utilisé par d'autres applications au sein de l'entreprise qui ont à voir avec les factures. Des méthodes telles que addItem (), removeItem () appartiennent à la classe de facture (qui est un objet métier) car elles sont indépendantes du cas d'utilisation. Maintenant, le code en ce qui concerne certains cas d'utilisation spécifiques, appartient à une autre classe connue sous le nom de contrôleur (ou interacteur). Je dirais que le calcul des frais fait partie du cas d'utilisation de la facturation et le mettrait probablement séparément. Cela est subjectif, car on peut également affirmer que ces règles ne sont pas spécifiques à cette application ou à ce cas d'utilisation.

1
Lefteris E