J'ai un peu de mal à concevoir des cours de façon très différente. J'ai lu que les objets exposent leur comportement, pas leurs données; par conséquent, plutôt que d'utiliser des getter / setters pour modifier des données, les méthodes d'une classe donnée doivent être des "verbes" ou des actions opérant sur l'objet. Par exemple, nous aurions dans un objet « Compte », les méthodes Withdraw()
et Deposit()
plutôt que , setAmount()
etc. Voir: Pourquoi les méthodes getter et setter sont mauvais .
Ainsi, par exemple, dans le cas d’une classe de clients qui conserve beaucoup d’informations sur le client, par exemple Nom, Date de naissance, Tél, Adresse, etc., comment éviter les getter / setters pour obtenir et définir tous ces attributs? Quelle méthode de type 'Behavior' peut-on écrire pour renseigner toutes ces données?
la source
name()
surCustomer
est aussi claire ou plus claire, qu'une méthode appeléegetName()
.Réponses:
Comme indiqué dans de nombreuses réponses et commentaires, les DTO sont appropriés et utiles dans certaines situations, notamment pour le transfert de données à travers des frontières (par exemple, la sérialisation en JSON pour l'envoi via un service Web). Pour le reste de cette réponse, j'ignorerai plus ou moins cela et parlerai des classes de domaine et de la manière dont elles peuvent être conçues pour minimiser (voire éliminer) les accesseurs et les installateurs, tout en restant utiles dans un projet de grande envergure. Je ne parlerai pas non plus de la raison pour laquelle il faut supprimer les accesseurs ou les setters, ni à quel moment , car ce sont des questions qui leur sont propres.
A titre d'exemple, imaginez que votre projet est un jeu de plateau comme Chess ou Battleship. Vous pouvez avoir différentes façons de représenter cela dans une couche de présentation (application console, service Web, interface graphique, etc.), mais vous disposez également d'un domaine principal. Une classe que vous pourriez avoir est
Coordinate
, représentant une position sur le conseil. La "mauvaise" façon de l'écrire serait:(Je vais écrire des exemples de code en C # plutôt qu'en Java, par souci de concision et parce que je le connais mieux. Espérons que cela ne pose pas de problème. Les concepts sont les mêmes et la traduction doit être simple.)
Retrait des setters: immuabilité
Alors que les sites publics et les setters sont potentiellement problématiques, les setters sont le plus "méchant" des deux. Ils sont aussi généralement les plus faciles à éliminer. Le processus est simple: définissez la valeur depuis le constructeur. Toutes les méthodes qui ont déjà muté l'objet doivent plutôt renvoyer un nouveau résultat. Alors:
Notez que cela ne protège pas contre les autres méthodes de la classe qui mutent X et Y. Pour être plus strictement immuable, vous pourriez utiliser
readonly
(final
en Java). Mais dans les deux cas - que vous rendiez vos propriétés véritablement immuables ou que vous empêchiez simplement la mutation publique directe par le biais de setters -, vous avez la possibilité de supprimer vos setters publics. Dans la grande majorité des situations, cela fonctionne très bien.Suppression des accesseurs, partie 1: concevoir pour le comportement
Ce qui précède est très bien pour les passeurs, mais pour ce qui est des Getters, nous nous sommes tiré dans le pied avant même de commencer. Notre processus consistait à réfléchir à ce qu'est une coordonnée - les données qu'elle représente - et à créer une classe autour de celle-ci. Au lieu de cela, nous aurions dû commencer par quel comportement nous avons besoin d'une coordonnée. Soit dit en passant, ce processus est facilité par TDD, qui extrait uniquement les classes de ce type dès que nous en avons besoin. Nous commençons par le comportement souhaité et travaillons à partir de là.
Supposons donc que le premier endroit où vous ayez eu besoin d'un
Coordinate
était la détection de collision: vous vouliez vérifier si deux pièces occupaient le même espace sur le tableau. Voici le "mal" moyen (constructeurs omis pour plus de brièveté):Et voici le bon moyen:
(
IEquatable
mise en oeuvre abrégée pour plus de simplicité). En concevant pour le comportement plutôt que pour la modélisation des données, nous avons réussi à supprimer nos accesseurs.Notez que ceci est également pertinent pour votre exemple. Vous utilisez peut-être un ORM, ou affichez des informations sur les clients sur un site Web ou quelque chose du genre, auquel cas une sorte de
Customer
DTO aurait probablement un sens. Mais ce n'est pas parce que votre système inclut des clients et qu'ils sont représentés dans le modèle de données que vous devez avoir uneCustomer
classe dans votre domaine. Peut-être que lorsque vous concevez un comportement, un comportement apparaîtra, mais si vous voulez éviter les accesseurs, ne créez pas un comportement préventif.Suppression de getters, partie 2: comportement externe
Ce qui précède est donc un bon début, mais tôt ou tard, vous rencontrerez probablement un comportement associé à une classe, qui dépend en quelque sorte de l'état de la classe, mais qui n'appartient pas à la classe. Ce type de comportement correspond généralement à la couche de service de votre application.
En prenant notre
Coordinate
exemple, vous voudrez éventuellement représenter votre jeu auprès de l'utilisateur, ce qui peut vouloir dire que vous dessinez à l'écran. Vous pouvez, par exemple, avoir un projet d'interface utilisateur quiVector2
représente un point à l'écran. Mais il serait inapproprié que laCoordinate
classe prenne en charge la conversion d'une coordonnée en un point à l'écran, ce qui apporterait toutes sortes de problèmes de présentation à votre domaine principal. Malheureusement, ce type de situation est inhérent à la conception OO.La première option , qui est très souvent choisie, consiste simplement à exposer les maudits getters et à dire au diable. Cela a l'avantage de la simplicité. Mais puisque nous parlons d’éviter les accesseurs, disons, par argument, nous rejetons celui-ci et voyons quelles autres options existent.
Une deuxième option consiste à ajouter une sorte de
.ToDTO()
méthode à votre classe. Quoi qu'il en soit, cela peut être nécessaire, ou similaire, par exemple, lorsque vous souhaitez enregistrer le jeu, vous devez capturer la quasi-totalité de votre état. Mais la différence entre le faire pour vos services et simplement accéder directement au getter est plus ou moins esthétique. Il a toujours autant de "mal".Une troisième option - que j'ai vue préconisée par Zoran Horvat dans quelques vidéos de Pluralsight - consiste à utiliser une version modifiée du modèle de visiteur. Il s’agit d’une utilisation et d’une variation assez inhabituelles du modèle et je pense que la distance parcourue par les gens variera énormément selon qu’il s’agit d’ajouter de la complexité sans créer de réel avantage ou si c’est un bon compromis pour la situation. L'idée est essentiellement d'utiliser le modèle de visiteur standard, mais les
Visit
méthodes prennent comme paramètres l'état dont elles ont besoin, au lieu de la classe qu'elles visitent. Des exemples peuvent être trouvés ici .Pour notre problème, une solution utilisant ce modèle serait:
Comme vous pouvez probablement le constater,
_x
et_y
ne sont plus vraiment encapsulés. Nous pourrions les extraire en créant unIPositionTransformer<Tuple<int,int>>
qui les retourne directement. Selon vos goûts, vous aurez peut-être l'impression que cela rend tout l'exercice inutile.Cependant, avec les getters publics, il est très facile de mal faire les choses, il suffit d'extraire les données directement et de les utiliser en violation de Tell, Don't Ask . Tandis que vous utilisez ce modèle, il est en fait plus simple de le faire correctement: lorsque vous souhaitez créer un comportement, vous commencez automatiquement par créer un type qui lui est associé. Les violations de la TDA seront manifestement malodorantes et nécessiteront probablement une solution plus simple et meilleure. En pratique, ces points rendent beaucoup plus facile de faire les choses correctement, OO, plutôt que la manière "perverse" que les getters encouragent.
Enfin , même si cela n’est pas évident au départ, il peut en fait exister des moyens d’exposer suffisamment de comportements dont vous avez besoin pour éviter de devoir exposer l’état. Par exemple, en utilisant notre version précédente
Coordinate
dont le seul membre public estEquals()
(en pratique, uneIEquatable
implémentation complète serait nécessaire ), vous pourriez écrire la classe suivante dans votre couche de présentation:Il se trouve, peut-être étonnamment, que tout le comportement dont nous avions vraiment besoin d'une coordonnée pour atteindre notre objectif était la vérification de l'égalité! Bien entendu, cette solution est adaptée à ce problème et fait des hypothèses sur l'utilisation / les performances de la mémoire. C'est juste un exemple qui correspond à ce domaine de problème particulier, plutôt qu'un plan pour une solution générale.
Et encore une fois, les opinions varieront selon qu’il s’agisse ou non d’une complexité inutile. Dans certains cas, une telle solution n'existe peut-être pas, ou elle peut s'avérer prohibitive ou complexe, auquel cas vous pouvez revenir aux trois solutions ci-dessus.
la source
Customer
classe pourrait -elle avoir pour être capable de muter son numéro de téléphone? Peut-être que le numéro de téléphone du client change et que je dois maintenir ce changement dans la base de données, mais rien de tout cela ne relève de la responsabilité d'un objet de domaine fournissant un comportement. Il s’agit d’un problème d’accès aux données, qui serait probablement traité avec un DTO et, par exemple, un référentiel.Customer
données de l'objet de domaine relativement récentes (en phase avec la base de données) relève de la gestion de son cycle de vie, qui n'est également pas de sa responsabilité, et finirait probablement par résider dans un référentiel, une usine ou un conteneur IOC. tout ce qui instancie l'Customer
art.Le moyen le plus simple d'éviter les paramètres est de transmettre les valeurs à la méthode du constructeur lorsque vous
new
montez l'objet. C'est également le modèle habituel lorsque vous souhaitez rendre un objet immuable. Cela dit, les choses ne sont pas toujours aussi claires dans le monde réel.Il est vrai que les méthodes devraient concerner le comportement. Cependant, certains objets, tels que Client, existent principalement pour contenir des informations. Ce sont les types d'objets qui bénéficient le plus des accesseurs et des passeurs; Si de telles méthodes n'étaient pas du tout nécessaires, nous les éliminerions tout simplement.
Lectures supplémentaires à quel
moment les Getters et les Setters sont-ils justifiés
la source
setEvil(null);
Il est parfaitement correct d'avoir un objet qui expose des données plutôt qu'un comportement. Nous l'appelons simplement un "objet de données". Le modèle existe sous des noms tels que Data Transfer Object ou Value Object. Si l'objet a pour objet de contenir des données, les accesseurs et les installateurs sont valides pour accéder aux données.
Alors, pourquoi quelqu'un dirait-il que "les méthodes de getter et de setter sont mauvaises"? Vous verrez cela souvent - quelqu'un prend une directive parfaitement valable dans un contexte spécifique, puis supprime le contexte afin d'obtenir un titre plus percutant. Par exemple, " privilégier la composition au détriment de l'héritage " est un bon principe, mais assez tôt, quelqu'un supprimera le contexte et écrira " Pourquoi s'étend est le mal " (hé, même auteur, quelle coïncidence!) Ou "l' héritage est mauvais et doit être détruit ».
Si vous examinez le contenu de l'article, celui-ci contient en fait des points valables, mais il étend simplement le point pour créer un titre de type clic. Par exemple, l'article indique que les détails de l'implémentation ne doivent pas être exposés. Ce sont les principes d’encapsulation et de masquage des données qui sont fondamentaux dans OO. Cependant, une méthode getter n’expose pas, par définition, les détails d’implémentation. Dans le cas d'un objet de données Client , les propriétés de Nom , Adresse, etc. ne constituent pas des détails d'implémentation, mais tout le but de l'objet et doivent faire partie de l'interface publique.
Lisez la suite de l'article auquel vous créez un lien, pour voir comment il suggère de définir des propriétés telles que 'name' et 'salaire' sur un objet 'Employee' sans utiliser les pécheurs. Il s’avère qu’il utilise un motif avec un 'exportateur' qui contient des méthodes appelées add name, add Salary qui, à leur tour, définit les champs du même nom ... Il finit donc par utiliser exactement le motif convention de dénomination différente.
Cela revient à penser que vous évitez les pièges des singletons en les renommant nominalement, tout en conservant la même implémentation.
la source
Pour transformer la
Customer
classe d'un objet de données, nous pouvons nous poser les questions suivantes sur les champs de données:Comment voulons-nous utiliser {champ de données}? Où est utilisé {champ de données}? L'utilisation de {champ de données} peut-elle et doit-elle être déplacée vers la classe?
Par exemple:
Quel est le but de
Customer.Name
?Réponses possibles, affichez le nom dans une page Web de connexion, utilisez le nom dans les mailings au client.
Ce qui conduit à des méthodes:
Quel est le but de
Customer.DOB
?Valider l'âge du client. Remises sur l'anniversaire du client. Mailings.
Compte tenu des commentaires, l'exemple d'objet
Customer
- à la fois en tant qu'objet de données et en tant qu'objet "réel" avec ses propres responsabilités - est trop large; c'est-à-dire qu'il a trop de propriétés / responsabilités. Ce qui conduit soit à de nombreux composants en fonction deCustomer
(en lisant ses propriétés), soit àCustomer
de nombreux composants. Peut-être existe-t-il différentes vues du client, peut-être que chacune devrait avoir sa propre classe 1 distincte :Le client dans le cadre d'
Account
opérations monétaires n'est probablement utilisé que pour:Account
s.Ce client n'a pas besoin des domaines tels que
DOB
,FavouriteColour
,Tel
et peut - être même pasAddress
.Le client dans le contexte d'un utilisateur se connectant à un site Web bancaire.
Les champs pertinents sont:
FavouriteColour
, qui pourrait prendre la forme d’une thématisation personnalisée;LanguagePreferences
, etGreetingName
Au lieu de propriétés avec des getters et des setters, celles-ci pourraient être capturées dans une seule méthode:
Le client dans le contexte du marketing et de la messagerie personnalisée.
Ici, ne pas s'appuyer sur les propriétés d'un objet de données, mais plutôt sur les responsabilités de l'objet; par exemple:
Le fait que cet objet client ait une
FavouriteColour
propriété et / ou uneAddress
propriété devient sans objet: l'implémentation utilise peut-être ces propriétés; mais il peut également utiliser certaines techniques d'apprentissage automatique et utiliser les interactions précédentes avec le client pour découvrir les produits susceptibles d'intéresser le client.1. Bien sûr, les classes
Customer
etAccount
étaient des exemples. Pour un exemple ou un exercice simple, diviser ce client peut être excessif, mais avec l'exemple du fractionnement, j'espère démontrer que la méthode permettant de transformer un objet de données en objet avec les responsabilités vont fonctionner.la source
Customer.FavoriteColor
?TL; DR
La modélisation du comportement est bonne.
La modélisation pour de bonnes (!) Abstractions est préférable.
Parfois, des objets de données sont requis.
Comportement et Abstraction
Il y a plusieurs raisons pour éviter les getters et les setters. Comme vous l'avez fait remarquer, l'une consiste à éviter de modéliser des données. C'est en fait la raison mineure. La principale raison est de fournir l'abstraction.
Dans votre exemple avec le compte bancaire, c’est clair: une
setBalance()
méthode serait très mauvaise, car l’établissement d’un solde n’est pas la raison pour laquelle un compte doit être utilisé. Le comportement du compte doit résumer autant que possible de son solde actuel. Il peut prendre en compte le solde lorsqu’il décide d’échouer un retrait, il peut donner accès au solde actuel, mais la modification de l’interaction avec un compte bancaire ne doit pas obliger l’utilisateur à calculer le nouveau solde. C'est ce que le compte devrait faire lui-même.Même une paire de
deposit()
etwithdraw()
méthodes n'est pas idéal pour modéliser un compte bancaire. Un meilleur moyen serait de ne fournir qu'unetransfer()
méthode prenant en compte un autre compte et un montant en argument. Cela permettrait à la classe de comptes de s’assurer de manière triviale que vous ne créez / détruisez pas accidentellement de l’argent dans votre système, cela fournirait une abstraction très utilisable et fournirait aux utilisateurs plus de renseignements, car cela obligerait à utiliser des comptes spéciaux. gagné / investi / perdu de l’argent (voir double comptabilité ). Bien sûr, chaque utilisation d’un compte n’a pas besoin de ce niveau d’abstraction, mais il vaut certainement la peine de prendre en compte le niveau d’abstraction que vos classes peuvent fournir.Notez que fournir l'abstraction et masquer les données internes n'est pas toujours la même chose. Presque toutes les applications contiennent des classes qui ne sont en réalité que des données. Les tuples, les dictionnaires et les tableaux en sont des exemples fréquents. Vous ne voulez pas cacher la coordonnée x d'un point à l'utilisateur. Il y a très peu d'abstraction que vous pouvez / devriez faire avec un point.
La classe client
Un client est certainement une entité de votre système qui devrait essayer de fournir des abstractions utiles. Par exemple, il devrait probablement être associé à un panier et la combinaison du panier et du client devrait permettre de commettre un achat, ce qui pourrait déclencher des actions telles que l’envoi des produits demandés, le paiement d’argent (compte tenu du paiement choisi). méthode), etc.
Le problème est que toutes les données que vous avez mentionnées ne sont pas uniquement associées à un client, elles sont également mutables. Le client peut déménager. Ils peuvent changer leur compagnie de carte de crédit. Ils peuvent changer leur adresse email et leur numéro de téléphone. Heck, ils peuvent même changer de nom et / ou de sexe! Ainsi, une classe de clients complète doit en effet fournir un accès de modification complet à tous ces éléments de données.
Pourtant, les setters peuvent / doivent fournir des services non négligeables: Ils peuvent assurer un format correct des e - mails-, la vérification des adresses postales, etc. adresses De même, les « apporteurs » peuvent fournir des services de haut niveau comme la fourniture de courrier électronique dans le-adresses
Name <[email protected]>
Format en utilisant les champs de nom et l’adresse e-mail déposée, ou en fournissant une adresse postale correctement formatée, etc. Bien entendu, le contenu de cette fonctionnalité de haut niveau dépend en grande partie de votre cas d'utilisation. C'est peut-être un peu exagéré ou cela peut demander à une autre classe de bien faire les choses. Le choix du niveau d'abstraction n'est pas facile.la source
En essayant de développer la réponse de Kasper, il est plus facile de se plaindre et d’éliminer les setters. Dans un argument plutôt vague, à la main (et heureusement humoristique):
Quand Customer.Name changera-t-il?
Rarement. Peut-être qu'ils se sont mariés. Ou est entré dans la protection des témoins. Mais dans ce cas, vous voudriez également vérifier et éventuellement changer leur lieu de résidence, leurs proches parents et d’autres informations.
Quand la date de naissance changerait-elle?
Seulement lors de la création initiale ou lors de la saisie de données. Ou si elles sont un joueur de baseball Domincan. :-)
Ces champs ne doivent pas être accessibles avec les paramètres habituels. Vous avez peut-être une
Customer.initialEntry()
méthode ou uneCustomer.screwedUpHaveToChange()
méthode qui nécessite des autorisations spéciales. Mais n'a pas deCustomer.setDOB()
méthode publique .En général, un client est lu à partir d'une base de données, d'une API REST, de XML, etc. Utilisez une méthode
Customer.readFromDB()
ou, si vous êtes plus strict en matière de SRP / séparation des problèmes, vous aurez un générateur séparé, par exemple unCustomerPersister
objet avec uneread()
méthode. En interne, ils définissent en quelque sorte les champs (je préfère utiliser l'accès aux packages ou une classe interne, YMMV). Mais encore une fois, évitez les setters publics.(Addendum en tant que question a quelque peu changé ...)
Supposons que votre application utilise beaucoup les bases de données relationnelles. Il serait insensé d'avoir
Customer.saveToMYSQL()
ou desCustomer.readFromMYSQL()
méthodes. Cela crée un couplage indésirable avec une entité concrète, non standard et susceptible de changer . Par exemple, lorsque vous modifiez le schéma ou passez à Postgress ou Oracle.Cependant, l' OMI, il est parfaitement acceptable pour quelques clients à une norme abstraite ,
ResultSet
. Un objet assistant séparé (que je vais appelerCustomerDBHelper
, qui est probablement une sous-classe deAbstractMySQLHelper
) connaît toutes les connexions complexes à votre base de données, connaît les détails d'optimisation complexes, connaît les tables, la requête, les jointures, etc. (ou utilise un ORM comme Hibernate) pour générer le ResultSet. Votre objet parle à laResultSet
, qui est une norme abstraite , peu susceptible de changer. Lorsque vous modifiez la base de données sous-jacente ou le schéma, le client ne change pas , contrairement à CustomerDBHelper . Si vous êtes chanceux, seul AbstractMySQLHelper est modifié. Il effectue automatiquement les modifications pour le client, le commerçant, l'expédition, etc.De cette façon, vous pouvez (peut-être) éviter ou réduire le besoin de getters et de setters.
Et, l’essentiel de l’article Holub, comparez ce qui précède avec ce que vous feriez si vous utilisiez des accesseurs et des setters pour tout et que vous changiez la base de données.
De même, supposons que vous utilisiez beaucoup de XML. IMO, vous pouvez coupler votre client à un standard abstrait, tel qu'un Python xml.etree.ElementTree ou un Java org.w3c.dom.Element . Le client obtient et se définit à partir de cela. Encore une fois, vous pouvez (peut-être) réduire le besoin d’obtenteurs et de passeurs.
la source
Le problème des getters et des setters peut être dû au fait qu'une classe peut être utilisée dans la logique métier d'une manière, mais vous pouvez également avoir des classes auxiliaires pour sérialiser / désérialiser les données d'une base de données, d'un fichier ou d'un autre stockage persistant.
Étant donné qu'il existe de nombreuses façons de stocker / récupérer vos données et que vous souhaitez dissocier les objets de données de leur mode de stockage, l'encapsulation peut être "compromise" en rendant ces membres publics, ou en les rendant accessibles via des accesseurs ce qui est presque aussi mauvais que de les rendre public.
Il y a différentes façons de contourner cela. Une façon est de rendre les données disponibles à un "ami". Bien que l'amitié ne soit pas héritée, ceci peut être surmonté par n'importe quel sérialiseur demandant les informations à un ami, c'est-à-dire le sérialiseur de base "transmettant" les informations.
Votre classe pourrait avoir une méthode générique "fromMetadata" ou "toMetadata". From-metadata construit un objet et peut donc être un constructeur. S'il s'agit d'un langage typé dynamiquement, les métadonnées sont assez standard pour un tel langage et constituent probablement le principal moyen de construire de tels objets.
Si votre langage est spécifiquement C ++, une solution consiste à avoir une "structure" publique de données, puis à donner à votre classe une instance de cette "structure" en tant que membre et en fait à toutes les données que vous allez stocker. récupérer pour y être stocké. Vous pouvez ensuite facilement écrire des "wrappers" pour lire / écrire vos données dans plusieurs formats.
Si votre langage est C # ou Java et que vous n'avez pas de "struct", vous pouvez le faire de la même manière, mais votre struct est maintenant une classe secondaire. Il n'y a pas de véritable concept de "propriété" des données ou de const-ness. Par conséquent, si vous distribuez une instance de la classe contenant vos données et qu'elle est entièrement publique, tout ce qui reste en attente peut la modifier. Vous pouvez le "cloner" bien que cela puisse coûter cher. Sinon, vous pouvez faire en sorte que cette classe ait des données privées, mais utilisez des accesseurs. Cela donne aux utilisateurs de votre classe un moyen détourné d'accéder aux données, mais ce n'est pas l'interface directe avec votre classe. Il s'agit en réalité d'un détail du stockage des données de la classe, ce qui constitue également un cas d'utilisation.
la source
La POO consiste à encapsuler et à cacher les comportements à l'intérieur d'objets. Les objets sont des boîtes noires. C'est une façon de concevoir quelque chose. L'actif est dans de nombreux cas, il n'est pas nécessaire de connaître l'état interne d'un autre composant et il est préférable de ne pas le savoir. Vous pouvez appliquer cette idée principalement avec des interfaces ou à l'intérieur d'un objet avec une visibilité et en veillant à ce que seuls les verbes / actions autorisés soient disponibles pour l'appelant.
Cela fonctionne bien pour une sorte de problème. Par exemple, dans les interfaces utilisateur pour modéliser des composants d'interface utilisateur individuels. Lorsque vous interagissez avec une zone de texte, vous ne vous préoccupez que de définir le texte, de l'obtenir ou d'écouter l'événement de changement de texte. Vous n'êtes généralement pas intéressé par l'emplacement du curseur, la police utilisée pour dessiner le texte ou l'utilisation du clavier. L'encapsulation en fournit beaucoup ici.
Au contraire, lorsque vous appelez un service réseau, vous fournissez une entrée explicite. Il y a généralement une grammaire (comme en JSON ou XML) et toute possibilité d'appeler le service n'a aucune raison d'être cachée. L'idée est que vous pouvez appeler le service comme vous le souhaitez et que le format des données est public et publié.
Dans ce cas, ou dans beaucoup d’autres (comme l’accès à une base de données), vous travaillez réellement avec des données partagées. En tant que tel, il n’ya aucune raison de le cacher, vous voulez au contraire le rendre disponible. Il peut y avoir un problème d’accès en lecture / écriture ou de cohérence de datacheck, mais à ce noyau, le concept de noyau s’il est public.
Pour une telle exigence de conception où vous souhaitez éviter l’encapsulation et rendre les choses publiques et claires, vous voulez éviter les objets. Ce dont vous avez vraiment besoin, ce sont des n-uplets, des structures C ou leurs équivalents, pas des objets.
Mais cela se produit également dans des langages tels que Java, les objets ou les tableaux d'objets ne peuvent être modélisés. Les objets eux-mêmes peuvent contenir quelques types natifs (int, float ...) mais c'est tout. Mais les objets peuvent aussi se comporter comme une simple structure avec seulement des champs publics et tout.
Donc, si vous modélisez des données, vous pouvez utiliser uniquement des champs publics à l'intérieur d'objets car vous n'en avez pas besoin de plus. Vous n'utilisez pas d'encapsulation parce que vous n'en avez pas besoin. Ceci est fait de cette façon dans beaucoup de langues. En java, historiquement, une rose standard où avec getter / setter vous pouviez au moins avoir un contrôle lecture / écriture (en n’ajoutant pas setter par exemple) et que les outils et le framework utilisant l’API instrospection rechercheraient des méthodes getter / setter et les utiliseraient remplir automatiquement le contenu ou afficher les thèses sous forme de champs modifiables dans l'interface utilisateur générée automatiquement.
Là aussi l'argument vous pouvez ajouter un peu de logique / vérification dans la méthode setter.
En réalité, il n’ya pratiquement aucune justification pour les getter / setters car ils sont le plus souvent utilisés pour modéliser des données pures. Les frameworks et les développeurs utilisant vos objets ne s’attendent pas à ce que le getter / setter ne fasse que définir / récupérer les champs de toute façon. En réalité, vous ne faites pas plus avec getter / setter que ce qui pourrait être fait avec des champs publics.
Mais c’est difficile de supprimer les vieilles habitudes et les vieilles habitudes… Vous pourriez même être menacé par vos collègues ou votre professeur si vous ne placez pas aveuglément des passeurs / passeurs partout s'ils ne possèdent pas le fond pour mieux comprendre ce qu’ils sont et ce qu’ils sont. ne pas.
Vous auriez probablement besoin de changer de langue pour vous débarrasser de tous ces codes de getters / setters. (Comme C # ou lisp). Pour moi, les getters / passeurs ne sont qu'une autre erreur d'un milliard de dollars ...
la source
@Getter @Setter class MutablePoint3D {private int x, y, z;}
.Je pense que cette question est épineuse parce que vous vous inquiétez des méthodes de comportement pour remplir les données, mais je ne vois aucune indication sur le comportement que la
Customer
classe d'objets est destinée à encapsuler.Ne confondez pas en
Customer
tant que classe d'objets avec «Client» en tant qu'utilisateur / acteur effectuant différentes tâches à l'aide de votre logiciel.Lorsque vous parlez d' une classe de clients qui conserve beaucoup d'informations sur le client, il semble que, dans la mesure du possible, votre classe de clients ne la distingue pas vraiment du roc. Un
Rock
peut avoir une couleur, vous pouvez lui donner un nom, vous pouvez avoir un champ pour stocker son adresse actuelle, mais nous n'attendons aucun comportement intelligent de la part d'un rocher.De l'article lié à propos des getters / setters étant pervers:
Si aucun comportement
Customer
n'est défini, le fait de désigner un rocher comme un mot ne change pas le fait qu'il ne s'agit que d'un objet avec certaines propriétés que vous souhaitez suivre, et peu importe les astuces que vous souhaitez jouer pour vous écarter des getters et les setters. Un rocher ne se soucie pas d'avoir un nom valide et on ne s'attendrait pas à ce qu'un rocher sache si une adresse est valide ou non.Votre système de commande peut associer un
Rock
à une commande d'achat et tant que celle-ciRock
a une adresse définie, une partie du système peut ainsi garantir qu'un article est livré à une pierre.Dans tous ces cas, il
Rock
s’agit simplement d’un objet de données et le restera jusqu’à ce que nous définissions des comportements spécifiques avec des résultats utiles au lieu d’hypothétiques.Essaye ça:
Lorsque vous évitez de surcharger le mot 'Client' avec 2 significations potentiellement différentes, cela devrait faciliter la conceptualisation.
Est-ce qu'un
Rock
objet passe un ordre ou est-ce quelque chose qu'un être humain fait en cliquant sur des éléments de l'interface utilisateur pour déclencher des actions dans votre système?la source
J'ajoute mes 2 centimes ici en mentionnant l' approche des objets parlant SQL .
Cette approche est basée sur la notion d'objet autonome. Il dispose de toutes les ressources nécessaires pour mettre en œuvre son comportement. Il n'est pas nécessaire de lui dire comment faire son travail - une requête déclarative suffit. Et un objet n'a certainement pas à contenir toutes ses données en tant que propriétés de classe. Ce n'est vraiment pas - et ne devrait pas - peu importe d'où ils viennent.
En parlant d'un agrégat , l'immuabilité n'est pas un problème. Disons que vous avez une séquence d’états que l’agrégat peut contenir: C’est parfaitement bien d’implémenter chaque état en tant qu’objet autonome. Vous pourriez probablement aller encore plus loin: discutez avec votre expert de domaine. Les chances sont qu'il ou elle ne voit pas cet agrégat comme une entité unifiée. Chaque état a probablement sa propre signification, méritant son propre objet.
Enfin, j'aimerais noter que le processus de recherche d'objet est très similaire avec la décomposition du système en sous-systèmes . Les deux sont basés sur le comportement, pas autre chose.
la source