Je travaille dans un projet qui traite des périphériques physiques, et je ne sais pas comment nommer correctement certaines classes de ce projet.
Étant donné que les appareils réels (capteurs et récepteurs) sont une chose et que leur représentation dans le logiciel en est une autre, je pense à nommer certaines classes avec le modèle de nom de suffixe "Info".
Par exemple, alors que a Sensor
serait une classe pour représenter le capteur réel (lorsqu'il est réellement connecté à un appareil en fonctionnement), SensorInfo
serait utilisé pour représenter uniquement les caractéristiques d'un tel capteur. Par exemple, lors de l'enregistrement du fichier, je sérialiserais un SensorInfo
dans l'en-tête du fichier, au lieu de sérialiser un Sensor
, ce qui n'aurait même pas de sens.
Mais maintenant, je suis confus, car il y a un point médian sur le cycle de vie des objets où je ne peux pas décider si je dois utiliser l'un ou l'autre, ou comment les obtenir les uns des autres, ou même si les deux variantes doivent en fait être réduites à une seule classe.
De plus, l'exemple de Employee
classe trop commun n'est évidemment qu'une représentation de la personne réelle, mais personne ne suggérerait de nommer la classe à la EmployeeInfo
place, pour autant que je sache.
Le langage avec lequel je travaille est .NET, et ce modèle de dénomination semble être commun dans tout le framework, par exemple avec ces classes:
Directory
etDirectoryInfo
cours;File
etFileInfo
cours;ConnectionInfo
classe (sansConnection
classe correspondante );DeviceInfo
classe (sansDevice
classe correspondante );
Ma question est donc la suivante: existe-t-il une justification commune à l'utilisation de ce modèle de dénomination? Y a-t-il des cas où il est logique d'avoir des paires de noms ( Thing
et ThingInfo
) et d'autres cas où il ne devrait exister que la ThingInfo
classe, ou la Thing
classe, sans son homologue?
la source
Info
suffixe distingue unestatic
classe contenant des méthodes utilitaires de son homologue avec état. Ce n'est pas une "meilleure pratique" en tant que telle; c'est juste un moyen que l'équipe .NET a trouvé pour résoudre un problème particulier. Ils auraient pu tout aussi facilement trouverFileUtility
etFile
, maisFile.DoSomething()
etFileInfo.FileName
semblent mieux lire.Foo
, vous pouvez avoir une classe utilitaire non instanciableFoos
. Lorsqu'il s'agit de nommer, l'important est la cohérence au sein de l'API, et idéalement entre les API de la plate-forme.Employee
des dizaines sont trouvés par des dizaines, en ligne ou dans des livres classiques, alors que je ne les ai pasEmployeeInfo
encore vus (peut-être parce qu'un employé est un être vivant, pas une construction technique comme une connexion ou un fichier). Mais, d'accord, si la classeEmployeeInfo
devait être proposée dans un projet, je pense qu'elle pourrait avoir son utilité.Réponses:
Je pense que "info" est un terme impropre. Les objets ont un état et des actions: "info" est juste un autre nom pour "état" qui est déjà intégré dans la POO.
Qu'essayez-vous vraiment de modéliser ici? Vous avez besoin d'un objet qui représente le matériel dans le logiciel afin qu'un autre code puisse l'utiliser.
C'est facile à dire, mais comme vous l'avez découvert, il y a plus que cela. "Représenter le matériel" est étonnamment large. Un objet qui fait cela a plusieurs préoccupations:
Certains périphériques tels que les capteurs auront moins de soucis qu'un périphérique multifonction imprimante / scanner / fax. Un capteur produit probablement un flux binaire, tandis qu'un appareil complexe peut avoir des protocoles et des interactions complexes.
Quoi qu'il en soit, revenons à votre question spécifique, il existe plusieurs façons de le faire en fonction de vos besoins spécifiques ainsi que de la complexité de l'interaction matérielle.
Voici un exemple de la façon dont je voudrais concevoir la hiérarchie des classes pour un capteur de température:
ITemperatureSource: interface qui représente tout ce qui peut produire des données de température: un capteur, peut même être un wrapper de fichier ou des données codées en dur (pensez: tests simulés).
Acme4680Sensor: capteur ACME modèle 4680 (idéal pour détecter quand le Roadrunner est à proximité). Cela peut implémenter plusieurs interfaces: ce capteur détecte peut-être à la fois la température et l'humidité. Cet objet contient un état au niveau du programme tel que "le capteur est-il connecté?" et "quelle a été la dernière lecture?"
Acme4680SensorComm: utilisé uniquement pour communiquer avec le périphérique physique. Il ne maintient pas beaucoup d'état. Il est utilisé pour envoyer et recevoir des messages. Il a une méthode C # pour chacun des messages que le matériel comprend.
HardwareManager: utilisé pour obtenir des appareils. Il s'agit essentiellement d'une fabrique qui met en cache des instances: il ne doit y avoir qu'une seule instance d'un objet périphérique pour chaque périphérique matériel. Il doit être suffisamment intelligent pour savoir que si le thread A demande le capteur de température ACME et le thread B demande le capteur d'humidité ACME, ce sont en fait le même objet et doivent être renvoyés aux deux threads.
Au niveau supérieur, vous disposerez d'interfaces pour chaque type de matériel. Ils décrivent les actions que votre code C # prendrait sur les périphériques, en utilisant des types de données C # (pas par exemple des tableaux d'octets que le pilote de périphérique brut pourrait utiliser).
Au même niveau, vous avez une classe d'énumération avec une instance pour chaque type de matériel. Le capteur de température peut être d'un type, le capteur d'humidité un autre.
Un niveau en dessous se trouve les classes réelles qui implémentent ces interfaces: elles représentent un périphérique similaire au capteur Acme4680 que j'ai décrit ci-dessus. Toute classe particulière peut implémenter plusieurs interfaces si le périphérique peut exécuter plusieurs fonctions.
Chaque classe de périphérique possède sa propre classe Comm (communication) privée qui gère la tâche de bas niveau de communication avec le matériel.
En dehors du module matériel, la seule couche visible est les interfaces / enum plus le HardwareManager. La classe HardwareManager est l'abstraction d'usine qui gère l'instanciation des classes de périphériques, les instances de mise en cache (vous ne voulez vraiment pas que deux classes de périphériques parlent au même périphérique matériel), etc. Une classe qui a besoin d'un type particulier de capteur demande au HardwareManager d'obtenir l'appareil pour l'énumération particulière, qu'il détermine ensuite s'il est déjà instancié, sinon comment le créer et l'initialiser, etc.
Le but ici est de dissocier la logique métier de la logique matérielle de bas niveau. Lorsque vous écrivez du code qui imprime des données de capteur à l'écran, ce code ne devrait pas se soucier du type de capteur que vous avez si et seulement si ce découplage est en place et centré sur ces interfaces matérielles.
Remarque: il existe des associations entre le HardwareManager et chaque classe de périphérique que je n'ai pas dessiné car le diagramme se serait transformé en soupe aux flèches.
la source
Il peut être un peu difficile de trouver une seule convention unificatrice ici parce que ces classes sont réparties sur un certain nombre d'espaces de noms (
ConnectionInfo
semble être dansCrystalDecisions
etDeviceInfo
dansSystem.Reporting.WebForms
).En regardant ces exemples, cependant, il semble y avoir deux utilisations distinctes du suffixe:
Distinguer une classe fournissant des méthodes statiques d'une classe fournissant des méthodes d'instance. C'est le cas des
System.IO
classes, comme le souligne leur description:Annuaire :
DirectoryInfo :
Info
semble être un choix un peu étrange ici, mais cela rend la différence relativement claire: uneDirectory
classe peut raisonnablement représenter un répertoire particulier ou fournir des méthodes d'assistance générales liées au répertoire sans détenir aucun état, alors qu'elleDirectoryInfo
ne peut vraiment être que la première.Soulignant que la classe ne contient que des informations et ne fournit pas de comportement qui pourrait raisonnablement être attendu du nom non suffixé .
Je pense que la dernière partie de cette phrase pourrait être la pièce du puzzle qui distingue, disons,
ConnectionInfo
deEmployeeInfo
. Si j'avais une classe appeléeConnection
, je m'attendrais raisonnablement à ce qu'elle me fournisse réellement les fonctionnalités d'une connexion - je chercherais des méthodes commevoid Open()
, etc. Cependant, personne sensé ne s'attendrait à ce qu'uneEmployee
classe puisse réellement faire ce que fait un vraiEmployee
, ou chercher des méthodes commevoid DoPaperwork()
oubool TryDiscreetlyBrowseFacebook()
.la source
En général, un
Info
objet encapsule des informations sur l'état d'un objet à un moment donné . Si je demande au système de regarder un fichier et de me donner unFileInfo
objet associé à sa taille, je m'attendrais à ce que cet objet indique la taille du fichier au moment où la demande a été donnée (ou, pour être plus précis, la taille de le fichier à un moment donné entre l'appel et le retour). Si la taille du fichier change entre le moment où la demande revient et le moment où l'FileInfo
objet est examiné, je ne m'attendrais pas à ce qu'une telle modification se reflète dans l'FileInfo
objet.Notez que ce comportement serait très différent de celui d'un
File
objet. Si une demande d'ouverture d'un fichier disque en mode non exclusif donne unFile
objet qui a uneSize
propriété, je m'attendrais à ce que la valeur renvoyée change ainsi lorsque la taille du fichier disque change, car l'File
objet ne représente pas simplement l'état de un fichier - il représente le fichier lui-même .Dans de nombreux cas, les objets qui s'attachent à une ressource doivent être nettoyés lorsque leurs services ne sont plus nécessaires. Étant donné que les
*Info
objets ne sont pas attachés aux ressources, ils ne nécessitent pas de nettoyage. Par conséquent, dans les cas où unInfo
objet satisfera aux exigences d'un client, il peut être préférable que le code en utilise un plutôt que d'utiliser un objet qui représenterait la ressource sous-jacente, mais dont la connexion à cette ressource devrait être nettoyée.la source
File
classe ne peut pas être instanciée et lesFileInfo
objets se mettent à jour avec le système de fichiers sous-jacent.FileInfo
simplement détenir des informations capturées statiquement. N'est-ce pas? De plus, je n'ai jamais eu l'occasion d'ouvrir des fichiers en mode non exclusif, mais cela devrait être possible, et je m'attendrais à ce qu'il existe une méthode qui signalera la taille actuelle d'un fichier, même si l'objet utilisé pour l'ouverture n'est pas appeléFile
(normalement je viens d' utiliserReadAllBytes
,WriteAllBytes
,ReadAllText
,WriteAllText
, etc.).Je n'aime pas cette distinction. Tous les objets sont des «représentations [dans le logiciel]». Voilà ce que signifie le mot «objet».
Maintenant, il peut être judicieux de séparer les informations sur un périphérique du code réel qui s'interface avec le périphérique. Ainsi, par exemple, un Sensor a-a SensorInfo, qui contient la plupart des variables d'instance, ainsi que certaines méthodes qui ne nécessitent pas de matériel, tandis que la classe Sensor est chargée d'interagir réellement avec le capteur physique. Vous n'avez pas-a à
Sensor
moins que votre ordinateur ne dispose d'un capteur, mais vous pourriez en avoir unSensorInfo
.Le problème est que ce type de conception peut être généralisé à (presque) n'importe quelle classe. Donc, vous devez être prudent. Vous n'auriez évidemment pas de
SensorInfoInfo
cours, par exemple. Et si vous avez uneSensor
variable, vous pourriez vous retrouver en train de violer la loi de Demeter en interagissant avec sonSensorInfo
membre. Bien sûr, rien de tout cela n'est fatal, mais la conception d'API n'est pas réservée aux auteurs de bibliothèques. Si vous gardez votre propre API propre et simple, votre code sera plus facile à gérer.Les ressources du système de fichiers comme les répertoires, à mon avis, sont très proches de ce bord. Il existe certaines situations dans lesquelles vous souhaitez décrire un répertoire qui n'est pas accessible localement, c'est vrai, mais le développeur moyen n'est probablement pas dans l'une de ces situations. À mon avis, compliquer la structure des classes est inutile. Comparez l'approche de Python dans
pathlib
: Il existe une seule classe qui "est probablement ce dont vous avez besoin" et diverses classes auxiliaires que la plupart des développeurs peuvent ignorer en toute sécurité. Si vous en avez vraiment besoin, cependant, ils fournissent en grande partie la même interface, juste avec des méthodes concrètes supprimées.la source
SensorInfo
serait une sorte de DTO, et / ou aussi comme un "instantané", ou même une "spécification", représentant uniquement la partie données / état de l'Sensor
objet réel que "peut-être même pas là-bas".PureWindowsPath
agit un peu comme un objet Info, mais il a des méthodes pour faire des choses qui ne nécessitent pas de système Windows (par exemple, prendre un sous-répertoire, séparer une extension de fichier, etc.). C'est plus utile que de simplement fournir une structure glorifiée.Je dirais que le contexte / domaine est important, car nous avons un code de logique métier de haut niveau et des modèles de bas niveau, des composants d'architecture, etc.
'Info', 'Data', 'Manager', 'Object', 'Class', 'Model', 'Controller' etc. peuvent être des suffixes malodorants, en particulier à un niveau inférieur, car chaque objet a des informations ou des données, donc cette information n'est pas nécessaire.
Les noms de classe du domaine commercial devraient être comme tous les intervenants en parlent, peu importe si cela semble bizarre ou n'est pas 100% correct.
Les bons suffixes pour les structures de données sont par exemple «Liste», «Carte» et pour les modèles de conseil «Décorateur», «Adaptateur» si vous pensez que cela est nécessaire.
Pour votre scénario de capteur, je ne m'attendrais pas
SensorInfo
à enregistrer ce qu'est votre capteur, maisSensorSpec
.Info
imho est plus une information dérivée, commeFileInfo
quelque chose comme la taille, vous ne sauvegardez pas ou le chemin de fichier qui est construit à partir du chemin et du nom de fichier, etc.Un autre point:
Cela me rappelle toujours de penser à un nom juste pendant quelques secondes et si je n'en trouve pas, j'utilise des noms étranges et je les marque «TODO». Je peux toujours le changer plus tard car mon IDE fournit un support de refactoring. Ce n'est pas un slogan d'entreprise qui doit être bon, mais juste du code que nous pouvons changer chaque fois que nous le voulons. Garde cela à l'esprit.
la source
ThingInfo peut être un excellent proxy en lecture seule pour la chose.
voir http://www.dofactory.com/net/proxy-design-pattern
Proxy: "Fournissez un substitut ou un espace réservé à un autre objet pour contrôler l'accès à celui-ci."
Typiquement, le ThingInfo aura des propriétés publiques sans setters. Ces classes et les méthodes de la classe sont sûres à utiliser et n'engageront aucune modification des données de sauvegarde, de l'objet ou de tout autre objet. Aucun changement d'état ou autre effet secondaire ne se produira. Ceux-ci peuvent être utilisés pour les rapports et les services Web ou partout où vous avez besoin d'informations sur l'objet mais souhaitez limiter l'accès à l'objet lui-même.
Utilisez le ThingInfo chaque fois que possible et limitez l'utilisation du Thing réel aux moments dont vous avez réellement besoin pour changer l'objet Thing. Cela rend la lecture et le débogage considérablement plus rapides lorsque vous vous habituez à utiliser ce modèle.
la source
Receiver
qui reçoit des flux de données de nombreuxSensor
s. L'idée est la suivante: le récepteur doit abstraire les capteurs réels. Mais le problème est: j'ai besoin des informations du capteur pour pouvoir les écrire dans un en-tête de fichier. La solution: chacunIReceiver
aura une liste deSensorInfo
. Si j'envoie des commandes au récepteur qui impliquent de changer l'état du capteur, ces changements seront reflétés (via getter) sur le respectifSensorInfo
.List<SensorInfo>
propriété en lecture seule.Jusqu'à présent, personne dans cette question ne semble avoir compris la véritable raison de cette convention de dénomination.
A
DirectoryInfo
n'est pas le répertoire. Il s'agit d' un DTO contenant des données sur l'annuaire. Il peut y avoir de nombreuses instances décrivant le même répertoire. Ce n'est pas une entité. Il s'agit d'un objet de valeur à jeter. ADirectoryInfo
ne représente pas le répertoire réel. Vous pouvez également le considérer comme un handle ou un contrôleur pour un répertoire.Comparez cela avec une classe nommée
Employee
. Il peut s'agir d'un objet entité ORM et il s'agit de l'objet unique décrivant cet employé. S'il s'agissait d'un objet de valeur sans identité, il devrait être appeléEmployeeInfo
. UnEmployee
représente effectivement l'employé réel. Une classe DTO de type valeur appeléeEmployeeInfo
ne représenterait clairement pas l'employé, mais plutôt la décrit ou stocke des données à son sujet.Il y a en fait un exemple dans la BCL où les deux classes existent: A
ServiceController
est une classe qui décrit un service Windows. Il peut y avoir n'importe quel nombre de ces contrôleurs pour chaque service. AServiceBase
(ou une classe dérivée) est le service réel et il n'a pas de sens conceptuel d'en avoir plusieurs instances par service distinct.la source
Proxy
modèle mentionné par @Shmoken, n'est-ce pas?EmployeeInfo
service Web. Ce n'est pas un proxy. Autre exemple: UnAddressInfo
ne même pas avoir une chose qu'il peut proxy , car une adresse est pas une entité. C'est une valeur autonome.