Que faut-il sur une liste de contrôle pour aider quelqu’un à développer un bon logiciel OO?

J’ai utilisé des langages et des techniques de programmation OO il y a des années (principalement en C ++), mais dans l’intervalle, je n’ai pas fait grand chose avec OO.

Je commence à faire un petit utilitaire en C #. Je pourrais simplement programmer tout cela sans utiliser de bonnes pratiques d’exploitation, mais ce serait un bon rappel pour moi d’appliquer les techniques d’exploitation.

Comme pour les niveaux de normalisation de firebase database, je recherche une liste de contrôle qui me rappelle les différentes règles de base pour un “bon” programme orienté object – une liste concise oui / non que je peux lire à l’occasion lors de la conception et de la mise en œuvre pour m’empêcher de penser et de travailler de manière procédurale. Serait encore plus utile si elle contenait les termes et concepts OO appropriés, de sorte que tout élément de contrôle puisse être recherché facilement pour des informations supplémentaires.

Que faut-il sur une liste de contrôle pour aider quelqu’un à développer un bon logiciel OO?

Inversement, quels «tests» pourraient être appliqués pour montrer que le logiciel n’est pas OO?

On dirait que vous voulez des questions de base oui / non à vous poser le long de votre chemin. Tout le monde a donné de superbes listes de “faire ceci” et de “penser comme ça”, alors voici ma réponse à quelques simples oui / non.

Puis-je répondre oui à toutes ces questions?

  • Mes classes représentent-elles les noms qui me concernent?
  • Mes classes fournissent-elles des méthodes pour les actions / verbes qu’elle peut effectuer?

Puis-je répondre non à toutes ces questions?

  • Est-ce que j’ai des données d’état global qui pourraient être soit placées dans un singleton, soit stockées dans des implémentations de classe qui fonctionnent avec?
  • Puis-je supprimer des méthodes publiques d’une classe et les append à une interface ou les rendre privées / protégées pour mieux encapsuler le comportement?
  • Devrais-je utiliser une interface pour séparer un comportement des autres interfaces ou de la classe d’implémentation?
  • Ai-je un code qui est répété entre les classes liées et que je peux déplacer dans une classe de base pour une meilleure réutilisation et une meilleure abstraction du code?
  • Est-ce que je teste le type de chose à décider de l’action à prendre? Si tel est le cas, ce comportement peut-il être inclus dans le type de base ou l’interface utilisée par le code en question pour permettre une utilisation plus efficace de l’abstraction ou le code en question devrait-il être remanié pour utiliser une meilleure classe ou interface de base?
  • Est-ce que je vérifie à plusieurs resockets certaines données de contexte pour décider du type à instancier? Si tel est le cas, cela peut-il être résumé dans un modèle de conception d’usine pour une meilleure abstraction de la logique et une réutilisation du code?
  • Une classe est-elle très grande avec plusieurs fonctionnalités? Si oui, puis-je le diviser en plusieurs classes, chacune ayant son propre objective?
  • Ai-je des classes non liées héritant de la même classe de base? Si c’est le cas, puis-je diviser la classe de base en meilleures abstractions ou puis-je utiliser la composition pour accéder aux fonctionnalités?
  • Ma hiérarchie d’inheritance est-elle devenue terriblement profonde? Si oui, puis-je l’aplatir ou séparer des éléments via des interfaces ou une fonctionnalité de fractionnement?
  • Je me suis inquiété beaucoup trop de ma hiérarchie d’inheritance?
  • Quand j’explique la conception à un canard en caoutchouc, est-ce que je me sens stupide à propos de la conception ou stupide de parler à un canard?

Juste quelques rapides sur mon esprit. J’espère que ça aide, OOP peut devenir assez fou. Je n’ai inclus aucun oui / non pour des éléments plus avancés, ce qui est généralement une préoccupation pour les applications plus volumineuses, comme l’dependency injection ou si vous devez scinder quelque chose en différentes couches de service / logique / assemblages … bien sûr, j’espère au moins séparez votre interface utilisateur de votre logique.

  • Les objects font des choses. (Point le plus important dans l’ensemble de la programmation orientée object!) Ne les considérez pas comme des “détenteurs de données” – envoyez-leur un message leur demandant de faire quelque chose. Quels verbes ma classe devrait-elle avoir? L’école de pensée “Responsibility-Driven Design” est shinye à cet égard. (Voir Conception des objects: rôles, responsabilités et collaborations , Rebecca Wirfs-Brock et Alan McKean, Addison-Wesley 2003, ISBN 0201379430.)
  • Pour chaque chose que le système doit faire, proposez un ensemble de scénarios concrets décrivant comment les objects se parlent pour effectuer le travail . Cela implique de penser en termes de diagrammes d’interaction et de jouer les appels de méthodes. – Ne commencez pas par un diagramme de classes – c’est une pensée SQL qu’une pensée OO.
  • Apprendre le développement piloté par les tests. Personne n’obtient son modèle d’object dès le départ, mais si vous utilisez TDD, vous mettez tout en œuvre pour vous assurer que votre modèle d’object fait ce qu’il doit et qu’il permet de refactoriser en toute sécurité lorsque les choses changeront par la suite.
  • Ne construisez que pour les besoins que vous avez maintenant – ne vous laissez pas emporter par la “réutilisation” ou des choses qui seront “utiles plus tard”. Si vous ne construisez que ce dont vous avez besoin pour le moment, vous gardez beaucoup plus d’espace de conception pour les tâches que vous pourriez faire plus tard.
  • Oubliez l’inheritance lorsque vous modélisez des objects. C’est juste une façon d’implémenter un code commun. Lorsque vous modélisez des objects, imaginez que vous examinez chaque object via une interface décrivant ce que vous pouvez lui demander de faire.
  • Si une méthode nécessite des charges de parameters ou si vous devez appeler plusieurs objects à plusieurs resockets pour obtenir beaucoup de données, il se peut que la méthode soit dans la mauvaise classe. Le meilleur endroit pour une méthode est juste à côté de la plupart des champs qu’elle utilise dans la même classe (ou la superclasse …)
  • Lisez un livre de modèles de conception pour votre langue. S’il s’agit de C #, essayez “Design Patterns in C #” de Steve Metsker. Cela vous apprendra une série de trucs que vous pouvez utiliser pour diviser le travail en plusieurs objects.
  • Ne testez pas un object pour voir de quel type il s’agit, puis agissez en fonction de ce type – c’est une odeur de code indiquant que l’object devrait probablement faire le travail. C’est un indice que vous devriez appeler l’object et lui demander de faire le travail. (Si seulement certains types d’objects font le travail, vous pouvez simplement avoir des implémentations “ne rien faire” dans certains objects … C’est de la POO légitime.)
  • En plaçant les méthodes et les données dans les bonnes classes, le code OO s’exécute plus rapidement (et donne ainsi aux machines virtuelles l’occasion d’optimiser leur travail) – ce n’est pas seulement esthétique ou théorique. L’étude Sharble et Cohen souligne ce fait – voir http://portal.acm.org/citation.cfm?doid=159420.155839 (voir le graphique des mesures relatives au “nombre d’instructions exécutées par scénario”)

Rassemblés dans divers livres, célèbres programmeurs C # et conseils généraux (pas grand-chose si ce n’est rien mien; c’est en ce sens que ce sont là diverses questions que je me pose au cours du développement, mais c’est tout)

  • Structs ou Classes? Si l’élément que vous créez est une valeur qui lui est propre, faites-en une structure. S’il s’agit d’un “object” avec des atsortingbuts et des sous-valeurs, des méthodes et éventuellement d’un état, faites-en un object.
  • Classes scellées : Si vous allez créer une classe et que vous n’avez pas explicitement besoin d’en hériter, rendez-la scellée. (Je le fais pour le gain de performance supposé)
  • Ne vous répétez pas : si vous vous retrouvez copié-collé (a / e), vous devriez probablement ( mais pas toujours ) repenser votre conception afin de minimiser la duplication de code.
  • Si vous n’avez pas besoin de fournir une implémentation de base pour une classe abstraite donnée, transformez-la en interface.
  • Le principe de spécialisation : chaque object que vous possédez ne devrait faire qu’une chose. Cela permet d’éviter “l’object divin”.
  • Utiliser les propriétés pour un access public : Cela a été débattu maintes et maintes fois, mais c’est vraiment la meilleure chose à faire. Les propriétés vous permettent de faire des choses que vous ne pouvez normalement pas faire avec des champs, et vous permettent également de mieux contrôler la manière dont l’object est obtenu et défini.
  • Singletons : un autre sujet controversé, et voici l’idée: ne les utilisez que lorsque vous en avez absolument besoin. La plupart du temps, un ensemble de méthodes statiques peut servir un singleton. (Bien que si vous avez absolument besoin d’un motif singleton, utilisez l’excellent modèle de Jon Skeet )
  • Couplage lâche : Assurez-vous que vos classes dépendent les unes des autres le moins possible; Assurez-vous qu’il est facile pour les utilisateurs de votre bibliothèque d’échanger des parties de votre bibliothèque avec d’autres (ou des parties construites sur mesure). Cela inclut l’utilisation d’interfaces, si nécessaire, d’Encapsulation (d’autres l’ont déjà mentionné) et de la plupart des autres principes de cette réponse.
  • Concevoir avec la simplicité à l’esprit : Contrairement au glaçage pour gâteaux, il est plus facile de concevoir quelque chose de simple maintenant et d’append plus tard que de concevoir un complexe maintenant et de le supprimer plus tard.

Je pourrais jeter tout ou partie de cela à la porte si je suis:

  • Écrire un projet personnel
  • vraiment pressé de faire quelque chose (mais j’y reviendrai plus tard … quelque temps …..;))

Ces principes aident à guider mon codage quotidien et ont considérablement amélioré la qualité de mon codage dans certains domaines! J’espère que cela pourra aider!

Le code complet 2 de Steve McConnell contient de nombreuses listes de contrôle prêtes à l’emploi pour une bonne construction du logiciel.

Les principes, les schémas et les pratiques agiles de C # de Robert C. Martin contiennent de nombreux principes pour une bonne conception des objects.

Les deux vous donneront une base solide pour commencer.

  • Les données appartiennent au code qui les opère (c’est-à-dire dans la même classe). Cela améliore la maintenabilité, car de nombreux champs et méthodes peuvent être privés ( encapsulation ) et sont donc, dans une certaine mesure, écartés de toute considération lors de l’parsing de l’interaction entre les composants.
  • Utilisez polymorphism au lieu de conditions – chaque fois que vous devez faire différentes choses en fonction de la classe d’un object, essayez de mettre ce comportement dans une méthode que différentes classes implémentent différemment, de sorte que vous n’avez qu’à appeler cette méthode.
  • Utilisez l’inheritance avec parcimonie, préférez la composition – L’inheritance est une caractéristique distinctive de la programmation OO et est souvent considéré comme “l’essence” de la POO. En fait, il est gravement surutilisé et devrait être classé parmi les éléments les moins importants.

L’une des meilleures sources serait le livre “Refactoring” de Martin Fowler, qui contient une liste (et des détails complémentaires) des odeurs de code orientées object que vous pourriez envisager de refactoriser.

Je recommanderais également les listes de contrôle du “Code propre” de Robert Martin.

  • Ai-je clairement défini les exigences? La documentation des exigences formelles n’est peut-être pas nécessaire, mais vous devez avoir une vision claire avant de commencer à coder. Les outils de mind mapping et les prototypes ou les croquis de conception peuvent constituer de bonnes alternatives si vous n’avez pas besoin d’une documentation formelle. Travaillez avec les utilisateurs finaux et les parties prenantes le plus tôt possible dans le processus logiciel pour vous assurer de mettre en œuvre ce dont ils ont besoin.

  • Est-ce que je réinvente la roue? Si vous codez pour résoudre un problème courant, recherchez une bibliothèque robuste qui résout déjà ce problème. Si vous pensez que vous avez peut-être déjà résolu le problème ailleurs dans votre code (ou qu’un collègue l’a peut-être déjà fait), cherchez d’abord une solution existante.

  • Mon object a-t-il un but clair et unique? Suivant le principe de l’encapsulation, un object doit avoir un comportement avec les données sur lesquelles il opère. Un object ne devrait avoir qu’une seule responsabilité majeure.

  • Puis-je coder vers une interface? Conception par contrat est un excellent moyen d’activer les tests unitaires, de documenter les exigences détaillées au niveau de la classe et d’encourager la réutilisation du code.

  • Puis-je mettre mon code sous test? Le développement piloté par les tests (TDD) n’est pas toujours facile; mais les tests unitaires sont très utiles pour la refactorisation et la vérification du comportement de régression après les modifications. Va de pair avec Design By Contract.

  • Est-ce que je suis en train de trop dessiner? N’essayez pas de coder un composant réutilisable. N’essayez pas d’anticiper les besoins futurs. Ces choses peuvent sembler contre-intuitives, mais elles conduisent à une meilleure conception. La première fois que vous codez quelque chose, implémentez-le aussi simplement que possible et faites-le fonctionner. La deuxième fois, vous utilisez la même logique, copiez et collez. Une fois que vous avez deux sections de code de travail avec une logique commune, vous pouvez facilement refactoriser sans essayer d’anticiper les besoins futurs.

  • Suis-je en train d’introduire du code redudant? Ne vous répétez pas (DRY) est le principal principe directeur de la refactorisation. Utilisez uniquement le copier-coller comme première étape du refactoring. Ne codez pas la même chose à différents endroits, c’est un cauchemar de maintenance.

  • S’agit-il d’un motif de conception, d’un anti-motif ou d’une odeur de code courants? Familiarisez-vous avec les solutions courantes aux problèmes de conception d’OO et recherchez-les au fur et à mesure que vous codez – mais n’essayez pas de forcer un problème à s’adapter à un modèle donné. Méfiez-vous des codes qui tombent dans un schéma commun de “mauvaise pratique”.

  • Mon code est-il trop étroitement lié? Le couplage en vrac est un principe qui tente de réduire les interdépendances entre deux classes ou plus. Certaines dépendances sont nécessaires; mais plus vous dépendez d’une autre classe, plus vous devez corriger lorsque cette classe change. Ne laissez pas le code d’une classe dépendre des détails d’implémentation d’une autre classe – utilisez un object uniquement conformément à son contrat.

  • Est-ce que j’expose trop d’informations? Informations de pratique se cachant. Si une méthode ou un champ n’a pas besoin d’être public, rendez-le privé. N’exposez que le minimum d’API nécessaire pour qu’un object remplisse son contrat. Ne rendez pas les détails d’implémentation accessibles aux objects client.

  • Est-ce que je code de façon défensive? Vérifiez les conditions d’erreur et Fail Fast. N’ayez pas peur d’utiliser des exceptions et laissez-les se propager. Si votre programme atteint un état inattendu, il est bien mieux d’abandonner une opération, de consigner une trace de stack pour que vous puissiez travailler et d’éviter un comportement imprévisible dans votre code en aval. Suivez les meilleures pratiques pour nettoyer les ressources, telles que l’instruction using() {} .

  • Pourrai-je lire ce code dans six mois? Un bon code est lisible avec une documentation minimale. Mettez des commentaires si nécessaire; mais écrivez aussi du code intuitif et utilisez des noms de classe, de méthode et de variable significatifs. Pratiquez un bon style de codage; si vous travaillez sur un projet d’équipe, chaque membre de l’équipe doit écrire un code qui a le même aspect.

  • Est-ce que ça marche encore? Testez tôt, testez souvent. Après avoir introduit une nouvelle fonctionnalité, revenez en arrière et touchez tout comportement existant qui pourrait avoir été affecté. Demandez à d’autres membres de l’équipe d’examiner vos pairs et de tester votre code. Relancez les tests unitaires après les modifications et maintenez-les à jour.

  • SOLIDE
  • SEC
  • TDD
  • Composition sur inheritance

Assurez-vous de lire et de comprendre ce qui suit

  • Encapsulation
    • (Assurez-vous de n’exposer que l’état et les fonctionnalités minimums nécessaires à l’exécution du travail)
  • Polymorphisme
    • (Capacité pour les objects dérivés de se comporter comme leurs parents)
  • La différence entre et interface et une classe abstraite
    • (Une classe abstraite permet aux fonctionnalités et aux états d’être partagés avec ses descendants, une interface n’est que la promesse que les fonctionnalités seront implémentées)

J’aime cette liste, même si elle peut être un peu dense pour être utilisée comme liste de contrôle.

UML – Unified Modeling Language, pour la modélisation d’objects et la définition de la structure et des relations entre les classes

http://en.wikipedia.org/wiki/Unified_Modeling_Language

Ensuite, bien sûr, les techniques de programmation pour OO (la plupart déjà mentionnées)

  • Masquage de l’information
  • Abstraction
  • Des interfaces
  • Encapsulation
  • Héritage / Polymorphisme

Certaines règles sont indépendantes de la langue, certaines diffèrent d’une langue à l’autre.

Voici quelques règles et commentaires qui contredisent certaines des règles précédemment publiées:

  • OO a 4 principes: Abstraction, Encapsulation, Polymorphisme et Héritage.
    Lisez à leur sujet et gardez-les à l’esprit.

  • Modélisation – Vos classes sont supposées modéliser des entités dans le domaine problématique:
    Diviser le problème en sous-domaines (packages / namespaces / assemblies)
    divisez ensuite les entités de chaque sous-domaine en classes.
    Les classes doivent contenir des méthodes qui modélisent ce que font les objects de ce type.

  • Utilisez UML, réfléchissez aux exigences, aux cas d’utilisation, puis aux diagrammes de classes, à leurs séquences. (applicable principalement à la conception de haut niveau – classes principales et processus.)

  • Modèles de conception – bon concept pour toute langue, la mise en œuvre diffère d’une langue à l’autre.

  • Struct vs. class – en C #, il s’agit de transmettre des données par valeur ou par référence.

  • Interface vs. base-class, est une classe de base, effectue une interface.

  • TDD – ce n’est pas un object OO, en fait, cela peut causer un manque de conception et conduire à beaucoup de réécriture. (Scrum par exemple recommande de ne pas le faire, pour XP c’est un must).

  • La reflection de C # remplace à certains égards OO (par exemple, la sérialisation basée sur la reflection), mais elle est nécessaire pour les frameworks avancés.

  • Assurez-vous que les classes ne “connaissent” pas d’autres classes à moins qu’elles ne soient obligées de le faire, il peut être utile de les diviser en plusieurs assemblages et de se limiter aux références.

  • AOP (Aspect Oriented Programming) améliore la POO (voir PostSharp, par exemple) d’une manière tellement révolutionnaire qu’il est préférable de la regarder au moins et de visionner son clip.

  • Pour C #, lisez les directives de MS (recherchez des directives dans l’index de l’aide de MSDN de VS). Elles contiennent de nombreuses directives et conventions utiles.

  • Livres recommandés:
    Directives de conception de cadre: Conventions, expressions et modèles pour les bibliothèques .NET réutilisables
    C # 3.0 en quelques mots