Déclaration de détermination
Je veux le lire petit à petit pendant un mois ou deux.
Ceci est mon propre mémo.
- 2020-07-05 Puisqu'il est devenu redondant, je vais le résumer en me concentrant davantage sur l'exemple de code après l'avoir lu.
Le point
Vue d'ensemble et introduction de la programmation orientée objet
1. Conception orientée objet
1.1 Éloge du design
- Si vous suivez la méthode de conception orientée objet, vous pouvez profiter du codage et produire des logiciels plus efficacement!
- Conception orientée objet = Gestion des dépendances.
--Les pièces
- objet
- Interaction
--Message
- Le but de la conception est de réduire les coûts de changement.
1.2 Outils de conception
- Principe de conception "SOLIDE"
- Modèle de conception "GoF"
1.3 Acte de conception
- La signification de «conception» est différente entre BUFD (cascade?) Et la conception orientée objet.
- Documentation complète qui identifie le comportement interne futur.
- Conception orientée objet
- Structure du code dans une petite zone, en supposant des changements.
1.4 Introduction facile de la programmation orientée objet
- Langage procédural
- Les variations ont un type de données.
- Les données et le comportement sont différents.
- Langage orienté objet
--L'objet a un comportement.
--Dispose de nombreux types (= classes).
- Vous pouvez prédire le comportement de l'objet.
- Les langages orientés objet peuvent étendre les types. Par conséquent, les applications orientées objet deviendront un langage de programmation spécial et unique géré par les programmeurs.
Focus sur la classe
2. Concevoir une classe de responsabilité unique
- Il existe deux critères différents pour l'objectif de la modélisation d'une application.
--Utilisez des classes pour effectuer les actions requises "immédiatement".
—— Facilitez le changement «après».
――La partie où la capacité technique de programmation apparaît clairement.
2.1 Décidez de ce qui appartient à la classe
- La première étape pour écrire du code VRAI est de vous assurer que vous avez une seule responsabilité (= minimale et utile).
2.2 Créer une classe avec une seule responsabilité
- Toutes les classes et méthodes ont une seule responsabilité.
――Qu'est-ce qu'une responsabilité unique?
- Lorsque vous paraphrasez la méthode de la classe en une question, cela devient une question significative.
- Vous pouvez expliquer la classe en une phrase.
- "It" et "ou" ne sont pas inclus.
2.3 Écrire un code qui accueille les changements
- Masquer les variables d'instance et les structures de données = changement de comportement (méthode).
- Empêchez les changements inattendus d'affecter votre code.
--Une fois que toutes les méthodes ont une seule responsabilité, la portée de la classe devient claire.
3. Gérer les dépendances
--Types de comportement d'objet
- Comportement que la classe elle-même devrait implémenter indépendamment
- Héritage du comportement
- Le comportement est implémenté dans d'autres objets
3-1. Comprendre les dépendances
- Des dépendances existent lorsque l'objet sait:
- Noms d'autres classes
- Le nom du message envoyé à d'autres objets que «self»
- Arguments requis par le message
- Ordre de ces arguments
- Minimisez les dépendances afin que les modifications de code ne soient pas suffisamment importantes pour forcer les modifications à d'autres objets.
3-2. Ecrire du code faiblement couplé
- La dépendance est mauvaise.
«La dépendance est comme une bactérie étrangère qui essaie de presser (concevoir) une classe.
- Lorsqu'il y a des noms d'autres classes
--Injection d'objets dépendants
- Vous n'avez pas besoin de connaître le nom de classe des autres objets et vous pouvez interrompre la jointure.
- Si vous gardez toujours à l'esprit ce dont vous dépendez et prenez l'habitude de les injecter, votre classe deviendra naturellement faiblement couplée.
- Isolement de la dépendance
--Isole la création de variables d'instance d'autres classes à partir du moment de l'initialisation de
self
, et crée-les dans la méthode définie indépendamment dans self
.
- La dépendance devient claire et la barrière à la réutilisation est abaissée.
-
||=
Opérateur (création d'instance différée)
- Lorsqu'un message autre que «soi» est envoyé
- Supprimez les dépendances externes et encapsulez dans une méthode dédiée
- La méthode devient indépendante des objets externes et dépend du message envoyé à
self
.
- Lorsqu'il y a des arguments demandés par le message
--Recevoir le hachage de l'option
--Toutes les dépendances sur l'ordre des arguments sont supprimées.
- Le nom "clé" dans le hachage devient la documentation explicite de l'argument.
- Le hachage renvoie
nil
pour les clés qui n'existent pas, vous pouvez donc définir explicitement une valeur par défaut.
- ||
opérateur, fetch
Méthode, merge
Méthode
- Lorsque l'ordre des arguments est fixe (lorsque la méthode qui doit dépendre est externe, etc.)
--Appuyez sur une méthode pour boucler l'interface externe.
--Créez un module
Wrapper
(=" usine ").
--Un objet dont le but est de créer d'autres objets
- En appelant une méthode externe dans la méthode de fabrique, vous pouvez éviter plusieurs dépendances sur des arguments d'ordre fixe.
--La fabrique n'est pas destinée à être instanciée.
- L'usine ne devrait pas être incluse dans d'autres classes.
3-3. Gestion de la direction des dépendances
- Dépendances inverses
--Sélection de la direction de dépendance
- "Dépendez de ce qui n'a pas changé de vous-même" = Trois faits qui sont à la base du concept suivant
- Certaines classes ont des exigences plus variables que d'autres
--Cadre avec développement actif, etc.
- Les classes concrètes sont plus susceptibles de changer que les classes abstraites
--Injection d'objets dépendants, etc.
- La dépendance à l'abstrait est toujours plus sûre que la dépendance au concret.
- Le changement de classes dépendantes depuis de nombreux endroits a un impact considérable
- Évitez les classes fortement dépendantes
- Trouver la dépendance en question
- Si la classe concrète a beaucoup de dépendances, vous devriez déclencher une alarme mentale.
«La clé de la maintenance est de contrôler le sens de la dépendance et de s'appuyer sur une classe qui change moins que vous.
De la conception centrée sur l'objet à la conception centrée sur le message
4. Créez une interface flexible
- La conception ne concerne pas seulement ce qu'un objet sait (responsabilité d'objet) ou qui il connaît (dépendances d'objet).
- Comment les objets se parlent.
4-1. Comprendre l'interface
- Interface publique
- Méthodes exposées à l'extérieur
- La classe implémente des méthodes, dont certaines sont destinées à être utilisées par d'autres objets.
4-2. Définir l'interface
--Caractéristiques de l'interface publique
- Clarifier les principales responsabilités de la classe
- Devrait être exécuté de l'extérieur
- Pas changé sur un coup de tête
- Sûr pour que les autres en dépendent
- Entièrement documenté dans le test
--Caractéristiques de l'interface privée
- Impliquez-vous dans les détails de mise en œuvre
- Ne devrait pas provenir d'autres objets
- Peut être changé pour n'importe quelle raison
- Il est dangereux pour les autres d'en dépendre
- Ne peut pas être mentionné dans le test
- L'interface publique est un contrat qui énonce clairement la responsabilité de la classe.
- Marquer une méthode comme publique ou privée indique à l'utilisateur de la classe de quelle méthode il dépendra probablement en toute sécurité.
4-3. Trouver une interface publique
-
Conception de l'application
-
"Objet de domaine"
--Une classe persistante qui représente un «nom» qui a à la fois des «données» et un «comportement».
—— Objets du monde réel grands et visibles qui sont finalement représentés dans la base de données.
-
Les objets de domaine ne sont pas essentiels à la conception d'applications.
-
Concentrez-vous sur les messages échangés entre les objets, pas sur les objets.
-
Vous devez d'abord déterminer les «objets» et les «messages» nécessaires pour répondre à votre cas d'utilisation.
--Utiliser un diagramme de séquence
―― Révélez l'échange de messages (interface publique) entre les classes et demandez: "Ce destinataire devrait-il être responsable de répondre à ce message?"
--Classes et qui et ce qu'ils savent => Décidez d'un message et où l'envoyer
-
"Je sais que j'ai besoin de ce cours, mais que dois-je faire?" => "J'ai besoin d'envoyer ce message, mais qui doit répondre?"
-
Demandez au destinataire "quoi" au lieu de dire à l'expéditeur "comment"
-
Passer la responsabilité de savoir «comment» à d'autres classes
-
La petite interface publique signifie qu'il n'y a que quelques méthodes qui en dépendent ailleurs.
-
Amélioration de la flexibilité et de la maintenabilité du code.
-
Recherche d'indépendance contextuelle
-
La meilleure situation est que l'objet est complètement indépendant de son contexte (en gardant l'objet capable de répondre aux messages détenus par d'autres classes).
-
Concentrez-vous sur la différence entre «quoi» et «comment»
--Injection d'objets dépendants
-
Techniques pour collaborer avec quelqu'un d'autre sans savoir qui il est
«J'espère que vous ferez confiance au destinataire du message et que vous vous comporterez de manière appropriée.
-
Importance de la confiance
-
"Je sais ce que je veux et je sais comment tu le fais" => "Je sais ce que je veux et toi Je sais quoi faire "=>" Je sais ce que je veux et je crois que vous ferez votre part. "
――Cette confiance de lâcher prise est la clé de la conception orientée objet
--Découvrir le besoin de nouveaux objets
- Lorsque le principe de la responsabilité unique est violé.
--Il indique aux autres objets comment il se comporte et nécessite une énorme quantité de contexte.
--Un nouvel objet est découvert car il devait lui envoyer un message
4-4. Écrivez le code qui expose le meilleur côté (interface)
―― L'interface définit l'application et détermine l'avenir.
- À chaque fois que vous créez une classe, déclarez l'interface.
--Méthodes incluses dans l'interface "publique"
- Identifier explicitement comme interface publique
- «Quoi» est plus que «comment»
- Le nom est immuable pour autant que l'on puisse penser
- Prenez un hachage comme argument facultatif
--Ruby public
, protected
, private
mots-clés
―― En utilisant ces mots-clés, vous pouvez transmettre les deux choses suivantes.
«Je crois que j'ai maintenant de meilleures informations que les informations dont disposent les" futurs "programmeurs.
«Je crois que nous devons empêcher les futurs programmeurs d'utiliser par inadvertance des méthodes que nous considérons instables.
―― Un certain nombre de programmeurs Ruby très talentueux osent omettre les mots-clés.
-
L'idée que vous pouvez utiliser des mots-clés pour restreindre l'accès à une méthode n'est qu'une illusion.
-
Lors de la création d'une interface publique, essayez de minimiser le contexte que l'interface publique exige des autres.
-
Faire en sorte que les expéditeurs de messages obtiennent ce qu'ils veulent sans savoir comment la classe implémente ce comportement.
-
Même si la personne qui l'a écrit en premier n'a pas défini l'interface publique, créez vous-même l'interface publique.
4-5. Loi de Déméter
- Une collection de règles de codage pour coupler librement des objets.
- Cela signifie que l'interface publique n'a pas été correctement identifiée et définie.
――Il y a des moments où il n'y a pas de préjudice réel même si vous enfreignez la loi.
- "Ne parlons qu'à nos voisins immédiats"
- "N'utilisons qu'un seul point"
- Obtenez des attributs distants via un objet intermédiaire.
- Identifiez explicitement les objets intermédiaires et modifiez-les si nécessaire.
--Utilisez la délégation.
- méthode
delegate
――Réfléchissez à la chaîne de messages en fonction de ce que vous recherchez
«Vous n'avez pas besoin de savoir" comment ".
- Si vous passez à une perspective basée sur un message et trouvez un message, ce message devient naturellement l'interface publique d'un objet.
―― Le message lui-même vous guidera vers ce qu'est l'objet.
5. Réduisez les coûts avec la saisie de canard
―― Qu'est-ce que le "typage canard"?
- Interface publique non liée à une classe particulière
-Juste comme Chameleon. "Si un objet sonne comme un canard et marche comme un canard, quelle que soit sa classe, c'est un canard."
5-1. Comprendre la saisie du canard
-
Certaines applications peuvent définir plusieurs interfaces qui couvrent plusieurs classes.
―― L'important n'est pas ce qu'est l'objet, mais ce qu'il fait
-
La capacité à ignorer les ambiguïtés sur une classe d'objets se révèle être un concepteur confiant.
-
Vous pourrez traiter un objet comme s'il était défini par son comportement plutôt que par sa classe.
Polymorphisme
--Une grande variété d'objets se réfèrent également à la capacité de répondre aux messages.
- Un message a plusieurs (poly) morphs.
5-2. Écrivez un code qui fait confiance à Duck
-
Conception difficile
―― Conscient que vous avez besoin d'un type de canard
-
Pour abstraire l'interface
-
Comment reconnaître un canard caché
- Une instruction de cas qui se branche dans une classe
- kind_of? Et is_a?
3. responds_to?
- Demandez ce que veut l'argument de cette méthode.
――La réponse à cette question montre le message à envoyer. À partir de ce message, le type de canard sous-jacent commence à être défini.
- N'incluez pas les dépendances inutiles que vous contrôlez plutôt que de faire confiance à d'autres objets.
-Faites confiance au canard.
--Une fois que vous connaissez le type de canard, définissez l'interface, implémentez-la là où vous en avez besoin et assurez-vous qu'elle se comporte correctement.
- Documentez le type de canard.
- Lors de la création d'un type de canard, vous devez à la fois documenter et tester son interface publique.
--Partagez le code entre le canard.
- Lors de la définition d'une méthode commune, ne partagez que l'interface, pas l'implémentation.
- Souvent, vous devez également partager certains comportements.
--Choisissez judicieusement un canard
- Comprenez que si le canard sous-jacent nécessite une modification des classes Ruby de base, il existe des risques et des compromis, et le but de la conception est de réduire les coûts.
5-3. Surmontez la peur de taper du canard
- Invalidation du type de canard par typage statique
--Comparaison du typage dynamique et statique
-
Saisie statique
-
Le compilateur trouve des erreurs de type au moment de la compilation <=> À moins que le compilateur ne vérifie les types, des erreurs de type à l'exécution se produisent
-
Les informations de type visualisées servent également de document. Sans le type <=>, le programmeur ne peut pas comprendre le code. Le programmeur ne peut pas déduire son type du contexte de l'objet
-
Le code compilé est optimisé et s'exécute rapidement <=> Sans un ensemble d'optimisations, votre application s'exécutera trop lentement
-
Typage dynamique
-
Le code est exécuté séquentiellement et chargé dynamiquement. Par conséquent, il n'y a pas de cycle de compilation / création. <=> Il est plus sûr de ne pas avoir de cycle de compilation / création pour l'ensemble du développement de l'application
-
Le code source ne contient pas d'informations de type explicites <=> Il est plus facile pour le programmeur de comprendre quand la déclaration de type n'est pas incluse dans le code. Le type de l'objet peut être déduit de ce contexte.
-
Métaprogrammation plus facile <=> La métaprogrammation est une fonctionnalité de langage souhaitable
-
C'est la propre intelligence du programmeur pour éviter les erreurs de type. L'idée que la saisie statique le rend sûr est une illusion.
«La bonté du code est la bonté du test après tout.
-
Le typage de canard est basé sur le typage dynamique.
6. Acquérir un comportement par héritage
6-1. Comprendre l'héritage de classe
- L'héritage est le mécanisme de "délégation de message automatique"
6-2. Identifier où l'héritage doit être utilisé
- Vous pouvez trouver des sous-classes, comme vous l'avez fait lorsque vous avez trouvé un type de canard.
--Si déclaration, type, catégorie, etc. qui confirment "les attributs qui détiennent leur propre classification".
--bad: "Je sais" qui vous êtes "car je sais" ce que vous faites "" => Indique l'existence d'une sous-classe.
- "Héritage unique". Les sous-classes ne peuvent avoir qu'une seule superclasse parente.
- Les sous-classes sont des superclasses "spécialisées".
- Les sous-classes ont toutes les interfaces publiques des superclasses.
6-3. Application incorrecte de l'héritage
- L'envoi de super héritera d'un comportement inutile et totalement dénué de sens.
6-4. Trouvez le résumé
- L'objet modélisé a une "relation de généralisation-spécialisation"
- Utilisez la bonne technique de codage
-Faire une superclasse abstraite
- Une superclasse n'est pas un objet complet en soi.
――Il est presque impensable d'envoyer un nouveau message à la super classe.
--Il existe également des langages de programmation orientés objet qui ont une syntaxe qui déclare explicitement une classe comme un résumé, comme le mot-clé Java ʻabstract`.
--Ruby n'a pas de tels mots-clés en raison de la nature de faire confiance aux autres.
- Des classes abstraites existent pour les sous-classes à créer, et c'est leur seul but.
- Fournit un emplacement de stockage commun pour les comportements partagés entre les sous-classes.
- Cela n'a aucun sens de créer une superclasse abstraite avec une seule sous-classe.
- Le moyen le plus simple d'identifier le bon résumé est lorsqu'il y a au moins trois classes concrètes.
-Promouvoir un comportement abstrait
- Lors de la refactorisation dans une nouvelle hiérarchie d'héritage, le code doit être structuré de sorte que l'abstrait puisse être promu, pas le concret.
- Résumé séparé du béton.
--Utilisez le modèle de méthode de modèle.
--Une technique qui définit la structure de base au sein d'une superclasse et envoie un message pour les contributions spécifiques à la sous-classe.
- Les super classes fournissent des sous-classes avec une structure (algorithme commun).
- Pour tous les objets, certaines méthodes utiliseront la même valeur initiale et certaines méthodes utiliseront des valeurs initiales différentes.
- Assurez-vous que chaque classe qui utilise le modèle de méthode de modèle a une implémentation pour chaque message qu'elle envoie.
- Les exigences des méthodes de modèle sont toujours documentées en implémentant des méthodes de correspondance qui soulèvent des erreurs utiles.
6-5. Gérer le degré de connectivité entre les superclasses et les sous-classes
- Les classes étroitement couplées sont en contact étroit les unes avec les autres, ce qui rend impossible de changer chacune indépendamment.
- Quand une sous-classe envoie
super
, c'est une déclaration qu'elle connaît effectivement l'algorithme de la superclasse et est" dépendante "de cette connaissance.
--Utilisez les messages hook pour coupler de manière lâche des sous-classes.
- Plutôt que de vous demander d'envoyer un
super
, demandez à la superclasse d'envoyer un message" hook "à la place.
- En utilisant la méthode hook, l'héritier peut fournir une spécialisation à la super classe sans forcer la transmission de «super».
7. Partager le comportement des rôles dans les modules
7-1. Comprendre les rôles
--Lorsque des objets non liés à l'origine jouent un rôle commun, les objets deviennent liés les uns aux autres. Le type de canard est un rouleau.
-
La méthode pour donner un nom et définir un groupe de méthodes est appelée un "module".
-
C'est un moyen idéal pour les objets de différentes classes de jouer un rôle commun avec du code défini au même endroit.
-
L'ensemble de messages auxquels un objet peut répondre comprend les quatre types de messages suivants.
--Messages que vous implémentez
--Messages implémentés par tous les objets de la hiérarchie au-dessus d'elle-même
--Messages ajoutés à lui-même et implémentés dans tous les modules
--Messages implémentés dans tous les modules ajoutés aux objets dans la hiérarchie au-dessus d'elle-même
-
Les objets doivent se gérer eux-mêmes
«Vous devez avoir votre propre comportement. Évitez d'ajouter des dépendances inutiles.
-
Les objets dépendants sont masqués en les injectant lors de l'initialisation.
-
En créant un module, d'autres objets peuvent utiliser ce module pour assumer des rôles sans dupliquer le code.
--Lors de l'inclusion de plusieurs modules dans une classe, la méthode du dernier module inclus arrive au début du chemin de recherche de méthode.
- Vous pouvez également ajouter la méthode d'un module à un seul objet en utilisant le mot-clé Ruby ʻextend
. --ʻExtend
ajoute le comportement du module directement à l'objet.
--Faire de la classe un module ʻextend`
- Une méthode de classe est ajoutée "à cette classe".
--Faire une instance de la classe ʻextend`
- Une méthode d'instance est ajoutée "à cette instance".
7-2. Ecrire du code héritable
--Anti-motif
- Un modèle dans lequel un objet utilise des noms de variables tels que «type» et «catégorie» pour déterminer le message à envoyer à lui-même.
- Le code commun doit être placé dans une superclasse abstraite et des sous-classes doivent être utilisées pour créer différents types.
- Un modèle dans lequel l'objet décide quel message envoyer après avoir vérifié la classe de l'objet qui reçoit le message.
- L'objet du destinataire doit implémenter une interface de type canard.
- Les types de canard partagent parfois un comportement, alors dans ce cas, mettez du code commun dans un module et incluez ce module dans chaque classe ou objet pour assumer le rôle.
--Il ne doit y avoir aucune sous-classe dans la superclasse abstraite qui n'utilise pas de code.
――Si vous ne pouvez pas restreindre l'abstrait à un correctement, ou s'il n'y a pas de code commun pouvant être abstrait, l'héritage ne peut pas résoudre le problème de conception.
- Les sous-classes promettent de remplacer les superclasses.
--Il n'est pas permis de laisser d'autres objets identifier leur type et décider quoi en faire ou à quoi s'attendre.
Principe de remplacement de Riskov
- "Pour que le système soit sain, le type dérivé doit être remplaçable par le supertype."
- En d'autres termes, comme Ruby, "les objets doivent se comporter comme ils le prétendent".
--Utilisez le modèle de méthode de modèle
-
Dans l'héritage concret de l'abstraction, la spécialisation est effectuée en remplaçant la méthode basée sur un modèle.
-
En utilisant la méthode du modèle, vous devez clairement décider ce qui change et ce qui ne change pas.
-
Évitez d'écrire du code qui appelle super du côté de l'héritage.
-
Utilisez plutôt les messages hook.
-
Vous pouvez rejoindre l'algorithme tout en étant libéré de la responsabilité de connaître l'algorithme de la classe abstraite.
8. Combinez des objets dans la composition
8-1. Composez le vélo à partir des pièces
--Having the relation "Bicycle has-a Parts" = Composition
--Créez une classe Parts abstraite.
8-2. Objet Compose Parts
- Faites une partie. Parts a plusieurs objets Part.
- Regroupez les objets Part individuels en objets Parts.
--Chaque tableau d'objets Part est un objet qui joue le rôle de Part.
--Pas une instance de la classe Part.
- Ce n'est pas le type de classe Part.
8-3. Fabrication de pièces
-"Usine"
--Un objet qui crée d'autres objets.
- Classe OpenStruct de Ruby
- Un moyen pratique de combiner plusieurs attributs en un seul objet.
- Struct
--Il est nécessaire de spécifier l'ordre et de passer les arguments au moment de l'initialisation.
- OpenStruct
- Effectuez un hachage lors de l'initialisation et extrayez-en les attributs.
8-4. Bicyclette composée
--Les pièces jouent le rôle de pièces
--OpenStruct prend le rôle de Part, qui implémente le nom, la description, les besoins_spare.
Agrégation - une composition spéciale
――Qu'est-ce que la composition?
--Deux objets ont une relation "has-a"
--Choses que l'objet contenu ne peut pas exister indépendamment de l'objet contenu
――Exemple: un plat a un apéritif, mais une fois que le plat est mangé, l'apéritif disparaît également.
―― Qu'est-ce que l'agrégation?
- L'existence de l'objet contenu est indépendante.
--Exemple: même si l'université disparaît et la faculté disparaît, les professeurs existent toujours.
8-5. Choix de composition et d'héritage
- Héritage de classe
- Au lieu de payer le coût de l'organisation des objets dans une structure hiérarchique, il n'y a pas de coût inhabituel pour le message.
--Composition
- Les objets peuvent désormais exister structurellement indépendamment, mais au prix d'une délégation de message explicite.
—— Les compositions ont beaucoup moins de dépendances que les héritages, donc hiérarchisez les compositions.
--Avantages de l'héritage
--VRAI.
--Open / Closed (Ouvert pour les extensions, fermé pour les correctifs)
- Coût d'héritage
- Si vous choisissez accidentellement l'héritage, vous devez dupliquer ou reconfigurer votre code.
- Peut être utilisé par d'autres programmeurs à des fins complètement inattendues.
--Avantages de la composition
-
Comme il est structurellement indépendant, il est hautement utilisable et peut être facilement utilisé même dans de nouveaux contextes inattendus.
――Il est très utile pour définir les règles d'assemblage d'un objet composé de pièces.
-
Coût de composition
-
Dépend de nombreuses parties.
――Même si les pièces individuelles sont petites et faciles à comprendre, le fonctionnement global de la combinaison n'est pas facile à comprendre.
--Sacrifiez la délégation automatique des messages.
-
Vous devez savoir explicitement quel message déléguer à qui.
Cela n'aide pas beaucoup pour le problème de la formation d'un code avec presque les mêmes parties.
--is-a Utiliser l'héritage pour les relations
- L'héritage est une spécialisation.
- L'héritage est le mieux adapté pour ajouter des fonctionnalités à une classe existante tout en utilisant la plupart de l'ancien code et en ajoutant une quantité relativement petite de nouveau code.
--Behaves-like-a Utiliser le type de canard pour les relations
- Lorsqu'un objet a un rôle, mais que ce rôle n'est pas la responsabilité principale de l'objet.
--Lorsque des objets indépendants partagent le désir de jouer le même rôle.
- Le comportement commun est défini dans les modules Ruby afin que les objets puissent assumer des rôles sans dupliquer le code.
--has-a Utiliser la composition pour les relations
-Lorsque de nombreux objets contiennent plusieurs parties et que la somme de ces objets dépasse la somme de ces parties.
-
Plus un objet a de parties, plus il est probable qu'il doive être modélisé dans une composition.
-
Apprendre à utiliser correctement des techniques telles que la composition, l'héritage par classe et le partage de comportements à l'aide de modules est une question d'expérience et de jugement.
Conception des tests
9. Concevoir des tests rentables
――Trois compétences sont indispensables pour pratiquer le code modifiable.
- Compréhension de la conception orientée objet.
- Bonne refactorisation de code.
――La refactorisation désigne le travail d'amélioration de la structure interne tout en conservant le comportement externe du logiciel.
- Capacité à rédiger des tests de grande valeur.
9-1. Test intentionnel
-
Intention de test
--Trouver un bug
-
Cela devient une spécification
-
Retarder les décisions de conception
-
Le test prouve que l'interface continue à se comporter correctement, de sorte que les modifications apportées au code sous-jacent ne nécessitent pas de réécrire le test.
-
Soutenir l'abstraction
-
Clarifier les défauts de conception
-
Rédigez moins de tests
-
Vous devez écrire le message défini par l'interface publique le plus stable.
9-2. Tester les messages entrants
-
Supprimez les tests pour les messages entrants qui ne sont pas dépendants.
-
Faites un double test.
-
Un faux objet qui assume le rôle. Une instance stylisée du porteur de rôle. Utilisé uniquement pour les tests.
--Double "stubs" la méthode. Autrement dit, implémentez une méthode qui renvoie une réponse pré-remplie.
9-3. Tester les méthodes privées
-
Les méthodes privées sont redondantes et instables (variables), et le temps de maintenance augmentera, alors ignorez-les.
-
Ne créez pas la méthode privée elle-même en premier lieu.
――Un objet qui a beaucoup de méthodes privées dégage l'odeur d'un design qui a trop de responsabilité.
-
Envisagez de découper ces objets en de nouveaux.
-
"N'écrivez jamais de méthodes privées. Si vous le faites, ne les testez jamais, sauf si, bien sûr, cela a du sens de le faire."
9-4. Tester les messages sortants
-
Le message sortant est soit une "requête", soit une "commande".
-
Les messages de requête n'ont d'importance que pour les objets qui les envoient.
-
Les messages de commande ont des effets visibles sur les autres objets de l'application.
-
Le test doit ignorer le message qui lui est envoyé.
-
Un message de requête qui sort doit également être ignoré.
-
Si vous avez injecté des objets dépendants à l'avance, vous pouvez facilement les remplacer par des faux.
-
En définissant des attentes sur le simulacre, vous pouvez prouver que l'objet testé remplit ses responsabilités sans dupliquer les déclarations qui appartiennent ailleurs.
9-5. Testez le type de canard
-
Si vous rencontrez du code qui utilise des anti-patterns mais qui n'a pas de tests, envisagez de le refactoriser avant d'écrire les tests pour une meilleure conception.
-
Le test de rôle ne doit être rédigé qu'une seule fois et partagé par tous les détenteurs.
--Minitest prend en charge le partage de tests avec les modules Ruby.
-
Découpez le comportement en modules.
-
Si vous traitez le test double comme n'importe quel autre porteur du rôle et prouvez son exactitude dans le test, vous pouvez éviter la fragilité du test et du talon sans vous soucier de l'impact.
-
Le désir de faire des tests de type canard a créé le besoin de tests partageables pour les rôles. Et une fois que vous avez une perspective basée sur ce rôle, vous pouvez l'utiliser dans diverses situations. Du point de vue de l'objet testé, tous les autres objets sont des rôles. Ensuite, traiter l'objet comme s'il s'agissait d'une représentation de ce rôle desserre le lien et augmente la flexibilité. Cela est vrai pour les applications et les tests.
9-6. Tester le code hérité
-
Principe de remplacement de Riskov
-
Le type dérivé doit être remplaçable par son type supérieur.
-
Le moyen le plus simple de prouver que vous suivez le principe de remplacement de Riskov est d'écrire des tests (de l'interface) partagés dans ce contrat commun et d'inclure des tests de tous les objets (dans des tests de classes concrètes). ..
-
Ecrire un test de comportement commun aux sous-classes.
-
En testant l'interface de la classe abstraite et en testant le comportement commun aux sous-classes, vous pouvez être sûr que les sous-classes ne sont pas hors du standard et que les nouveaux membres pourront créer des sous-classes en toute sécurité. Les nouveaux programmeurs n'ont pas à rechercher des superclasses pour creuser leurs besoins. Lorsque vous dessinez une nouvelle sous-classe, incluez simplement ces tests.
-
La création d'une instance d'une classe abstraite est soit difficile, soit assez difficile, soit impossible.
-
Lors du test de pièces spécifiques à une sous-classe, il est important de ne pas intégrer de connaissances de superclasse dans le test.
-
Si la superclasse utilise des méthodes de gabarit pour acquérir une spécialisation concrète, elle peut stuber le comportement normalement fourni par les sous-classes.
-
L'idée de créer une sous-classe pour préparer un stub est utile dans de nombreuses situations, et vous pouvez utiliser cette technique dans n'importe quel test tant que vous ne rompez pas le principe de remplacement de Liskov.
-
Les tests de structure d'héritage soigneusement écrits sont faciles. Écrivez un test partageable pour toute l'interface et l'autre pour les responsabilités de sous-classe.
――Séparez les responsabilités une par une.
-
Lorsque vous testez des spécialisations de sous-classe, veillez à ne pas divulguer vos connaissances de superclasse aux tests de sous-classe.
-
Il est difficile de tester la super classe abstraite.
-
Lors de l'utilisation du principe de remplacement de Riskov et de la création de sous-classes de test uniquement, appliquez également le test de responsabilité de la sous-classe à ces sous-classes.
-
Les meilleurs tests sont vaguement couplés avec le code en question, testés une seule fois pour tout et effectués au bon endroit.