Je me posais des questions sur une bonne conception de classe orientée objet. En particulier, j'ai du mal à choisir entre ces options:
- méthode statique vs instance
- méthode sans paramètres ni valeur de retour vs méthode avec paramètres et valeur de retour
- chevauchement vs fonctionnalité de méthode distincte
- méthode privée vs méthode publique
Exemple 1:
Cette implémentation utilise des méthodes d'instance, sans valeur ni paramètre de retour, sans fonctionnalité qui se chevauchent et toutes les méthodes sont publiques
XmlReader reader = new XmlReader(url);
reader.openUrl();
reader.readXml();
Document result = reader.getDocument();
Exemple 2:
Cette implémentation utilise des méthodes statiques, avec des valeurs et des paramètres de retour, avec des fonctionnalités qui se chevauchent et des méthodes privées
Document result = XmlReader.readXml(url);
Dans l'exemple un, toutes les méthodes sont publiques, ce qui facilite leur test unitaire. Bien que toutes les méthodes soient distinctes, readXml () dépend de openUrl () dans la mesure où openUrl () doit être appelé en premier. Toutes les données sont déclarées dans les champs d'instance, il n'y a donc pas de valeurs de retour ou de paramètres dans aucune méthode, sauf dans le constructeur et les accesseurs.
Dans l'exemple deux, une seule méthode est publique, les autres sont statiques privées, ce qui les rend difficiles à tester. Les méthodes se chevauchent en ce que readXml () appelle openUrl (). Il n'y a pas de champs, toutes les données sont passées comme paramètres dans les méthodes et le résultat est retourné immédiatement.
Quels principes dois-je suivre pour faire une programmation orientée objet appropriée?
la source
Réponses:
L'exemple 2 est assez mauvais pour les tests ... et je ne veux pas dire que vous ne pouvez pas tester les internes. Vous ne pouvez pas non plus remplacer votre
XmlReader
objet par un objet factice car vous n'avez aucun objet du tout.L'exemple 1 est inutilement difficile à utiliser. Qu'en est-il de
qui n'est pas plus difficile à utiliser que votre méthode statique.
Des choses comme l'ouverture de l'URL, la lecture de XML, la conversion d'octets en chaînes, l'analyse, la fermeture de sockets, etc., sont sans intérêt. Créer un objet et l'utiliser est important.
Donc, à mon humble avis, la conception OO appropriée est de rendre uniquement les deux choses publiques (sauf si vous avez vraiment besoin des étapes intermédiaires pour une raison quelconque). La statique est mauvaise.
la source
Document result = new XmlReader(url).getDocument();
Pourquoi? Pour que je puisse le mettre à jourDocument result = whateverReader.getDocument();
et me lewhateverReader
remettre par autre chose.Voici la chose - il n'y a pas une seule bonne réponse, et il n'y a pas de définition absolue de «conception orientée objet appropriée» (certaines personnes vous en proposeront une, mais elles sont naïves ... donnez-leur du temps).
Tout se résume à vos OBJECTIFS.
Vous êtes artiste et le papier est vierge. Vous pouvez dessiner un portrait latéral noir et blanc délicat et finement crayonné, ou une peinture abstraite avec d'énormes entailles de néons mélangés. Ou quoi que ce soit entre les deux.
Alors, QUI SE SENT BIEN pour le problème que vous résolvez? Quelles sont les plaintes des personnes qui ont besoin d'utiliser vos cours pour travailler avec xml? Qu'est-ce qui est difficile dans leur travail? Quel type de code essaient-ils d'écrire qui entoure les appels vers votre bibliothèque, et comment pouvez-vous aider à mieux l'écouler pour eux?
Souhaiteraient-ils plus de concision? Souhaiteraient-ils qu'il soit très intelligent pour déterminer les valeurs par défaut des paramètres, afin qu'ils n'aient pas à spécifier beaucoup (ou quoi que ce soit) et que cela devine correctement? Pouvez-vous automatiser les tâches de configuration et de nettoyage nécessaires à votre bibliothèque pour qu'il leur soit impossible d'oublier ces étapes? Que pouvez-vous faire d'autre pour eux?
Enfer, ce que vous devez probablement faire est de le coder de 4 ou 5 façons différentes, puis mettez votre chapeau de consommateur et écrivez le code qui utilise les 5, et voyez ce qui se sent mieux. Si vous ne pouvez pas le faire pour l'ensemble de votre bibliothèque, faites-le pour un sous-ensemble. Et vous devez également ajouter des alternatives supplémentaires à votre liste - qu'en est-il d'une interface fluide, ou d'une approche plus fonctionnelle, ou de paramètres nommés, ou de quelque chose basé sur DynamicObject afin que vous puissiez créer des "pseudo-méthodes" significatives qui les aident en dehors?
Pourquoi jQuery est-il le roi en ce moment? Parce que Resig et l' équipe ont suivi ce processus, jusqu'à ce qu'ils sont tombés sur un principe syntaxique incroyablement réduit la quantité de code JS qu'il faut pour travailler avec le Royaume et les événements. Ce principe syntaxique n'était pas clair pour eux ni pour personne d'autre lorsqu'ils ont commencé. Ils l'ont TROUVÉ.
En tant que programmeur, c'est ce que vous appelez le plus. Vous tâtonnez dans le noir en essayant des choses jusqu'à ce que vous le trouviez. Quand vous le ferez, vous saurez. Et vous donnerez à vos utilisateurs un énorme bond en productivité. Et c'est à cela que sert la conception (dans le domaine du logiciel).
la source
La deuxième option est meilleure car elle est plus simple à utiliser (même si ce n'est que vous), beaucoup plus simple.
Pour les tests unitaires, je testerais simplement l'interface et non les internes si vous voulez vraiment déplacer les internes de privé à protégé.
la source
1. Statique vs instance
Je pense qu'il existe des directives très claires sur ce qu'est une bonne conception OO et ce qui ne l'est pas. Le problème est que la blogosphère rend difficile de séparer le bon du mauvais et du laid. Vous pouvez trouver une sorte de référence soutenant même la pire pratique à laquelle vous pouvez penser.
Et la pire pratique à laquelle je peux penser est Global State, y compris les statistiques que vous avez mentionnées et le Singleton préféré de tout le monde. Quelques extraits de l' article classique de Misko Hevery sur le sujet .
Cela revient à dire que vous ne devez pas fournir de références statiques à tout ce qui a une sorte d'état stocké. Le seul endroit où j'utilise la statique est pour les constantes énumérées, et j'ai des doutes à ce sujet.
2. Méthodes avec paramètres d'entrée et valeurs de retour vs méthodes sans aucun
La chose que vous devez comprendre est que les méthodes qui n'ont ni paramètres d'entrée ni paramètres de sortie sont à peu près garanties de fonctionner sur une sorte d'état stocké en interne (sinon, que font-elles?). Il existe des langages entiers qui sont construits sur l'idée d'éviter l'état stocké.
Chaque fois que vous avez stocké l'état, vous avez la possibilité d'effets secondaires, alors assurez-vous de toujours l'utiliser en pleine conscience. Cela implique que vous devriez préférer les fonctions avec des entrées et / ou sorties définies.
Et, en fait, les fonctions qui ont des entrées et des sorties définies sont beaucoup plus faciles à tester - vous n'avez pas besoin d'exécuter une fonction ici et d'aller là-bas pour voir ce qui s'est passé, et vous n'avez pas besoin de définir une propriété quelque part sinon avant d'exécuter la fonction en cours de test.
Vous pouvez également utiliser en toute sécurité ce type de fonction comme statique. Cependant, je ne le ferais pas, car si je voulais ensuite utiliser une implémentation légèrement différente de cette fonction quelque part, plutôt que de fournir une instance différente avec la nouvelle implémentation, je suis coincé sans aucun moyen de remplacer la fonctionnalité.
3. Chevauchement vs Distinction
Je ne comprends pas la question. Quel serait l'avantage de 2 méthodes qui se chevauchent?
4. Privé vs public
N'exposez rien que vous n'avez pas besoin d'exposer. Cependant, je ne suis pas non plus un grand fan du privé. Je ne suis pas un développeur C #, mais un développeur ActionScript. J'ai passé beaucoup de temps dans le code Flex Framework d'Adobe, qui a été écrit vers 2007. Et ils ont fait de très mauvais choix sur ce qui devait être privé, ce qui en fait une sorte de cauchemar à essayer d'étendre leurs classes.
Donc, à moins que vous ne pensiez être un meilleur architecte que les développeurs d'Adobe vers 2007 (d'après votre question, je dirais que vous avez encore quelques années avant d'avoir la possibilité de faire cette affirmation), vous voudrez probablement simplement utiliser par défaut la protection .
Il y a quelques problèmes avec vos exemples de code qui signifient qu'ils ne sont pas bien architecturés, il n'est donc pas possible de choisir A ou B.
D'une part, vous devriez probablement séparer la création de votre objet de son utilisation . Donc, vous n'auriez généralement pas votre
new XMLReader()
droit à côté de l'endroit où il est utilisé.En outre, comme le dit @djna, vous devez encapsuler les méthodes utilisées dans vos utilisations de XML Reader, afin que votre API (exemple d'instance) puisse être simplifiée pour:
Je ne sais pas comment C # fonctionne, mais comme j'ai travaillé avec un certain nombre de technologies Web, je soupçonnerais que vous ne seriez pas toujours en mesure de renvoyer un document XML immédiatement (sauf peut-être comme promesse ou type futur) objet), mais je ne peux pas vous donner de conseils sur la façon de gérer une charge asynchrone en C #.
Notez qu'avec cette approche, vous pouvez créer plusieurs implémentations qui peuvent prendre un paramètre qui leur indique où / quoi lire et retourner un objet XML, et les échanger en fonction des besoins de votre projet. Par exemple, vous pouvez lire directement à partir d'une base de données, d'un magasin local ou, comme dans votre exemple d'origine, d'une URL. Vous ne pouvez pas faire cela si vous utilisez une méthode statique.
la source
Concentrez-vous sur le point de vue du client.
Les méthodes statiques ont tendance à limiter l'évolution future, notre client travaille en termes d'interface fournie par de nombreuses implémentations différentes.
Le problème majeur avec votre idiome ouvert / lu est que le client a besoin de connaître l'ordre dans lequel appeler les méthodes, quand il veut seulement faire un travail simple. C'est évident ici, mais dans une classe plus grande, c'est loin d'être évident.
La principale méthode à tester est read (). Les méthodes internes peuvent être rendues visibles pour tester les programmes en les rendant ni publics ni privés et en plaçant les tests dans le même package - les tests peuvent toujours être séparés du code publié.
la source
Dans la pratique, vous constaterez que les méthodes statiques sont généralement limitées aux classes utilitaires et ne devraient pas encombrer vos objets de domaine, gestionnaires, contrôleurs ou DAO. Les méthodes statiques sont plus utiles lorsque toutes les références nécessaires peuvent raisonnablement être transmises en tant que paramètres et offrent certaines fonctionnalités qui peuvent être réutilisées dans de nombreuses classes. Si vous vous retrouvez à utiliser une méthode statique comme solution de contournement pour avoir une référence à un objet d'instance, demandez-vous pourquoi vous n'avez pas simplement cette référence à la place.
Si vous n'avez pas besoin de paramètres sur la méthode, ne les ajoutez pas. Il en va de même avec la valeur de retour. Garder cela à l'esprit simplifiera votre code et vous assurera que vous ne codez pas pour une multitude de scénarios qui ne finissent jamais par se produire.
C'est une bonne idée d'essayer d'éviter les fonctionnalités qui se chevauchent. Parfois, cela peut être difficile, mais lorsqu'un changement de logique est nécessaire, il est beaucoup plus facile de changer une méthode qui est réutilisée, que de changer tout un tas de méthodes avec des fonctionnalités similaires
Les getters, setters et constructeurs devraient être publics. Tout ce que vous voudrez essayer de garder privé, sauf s'il existe un cas où une autre classe doit l'exécuter. Garder les méthodes par défaut sur privé aidera à maintenir l' encapsulation . Il en va de même pour les champs, habituez-vous à privé par défaut
la source
Je ne répondrai pas à votre question, mais je pense qu'un problème est créé par les termes que vous avez utilisés. Par exemple
Je pense que vous avez besoin d'un XML donc je vais créer un objet XML qui peut être créé à partir d'un type textuel (je ne connais pas C # ... en Java ça s'appelle String). Par exemple
Vous pouvez le tester et c'est clair. Ensuite, si vous avez besoin d'une usine, vous pouvez ajouter une méthode d'usine (et elle peut être statique comme votre 2ème exemple). Par exemple
la source
Vous pouvez suivre quelques règles simples. Cela aide si vous comprenez les raisons des règles.
méthode statique vs instance
Pour les méthodes, vous n'avez pas à prendre cette décision consciemment. S'il apparaît que votre méthode n'utilise aucun membre de champ (votre analyseur préféré devrait vous le dire), vous devez ajouter le mot clé statique.
méthode sans paramètres ni valeur de retour vs méthode avec paramètres et valeur de retour
La deuxième option est meilleure en raison de la portée. Vous devez toujours garder votre portée étroite. Tendre la main pour les choses dont vous avez besoin est mauvais, vous devez avoir une entrée, y travailler localement et retourner un résultat. Votre première option est mauvaise pour la même raison que les variables globales sont généralement mauvaises: elles ne sont significatives que pour une partie de votre code mais elles sont visibles (et donc du bruit) partout ailleurs et peuvent être altérées de n'importe où. Cela rend difficile d'obtenir une image complète de votre logique.
chevauchement vs fonctionnalité de méthode distincte
Je ne pense pas que ce soit un problème. Les méthodes appelant d'autres méthodes sont très bien si cela découpe les tâches en plus petits morceaux distinctement fonctionnels.
méthode privée vs méthode publique
Rendez tout privé sauf si vous en avez besoin pour qu'il soit public. L'utilisateur de votre classe peut se passer du bruit, il ne veut voir que ce qui compte pour lui.
la source