Lors d'un récent entretien d'embauche, je n'ai pas pu répondre à une question sur SOLIDE - au-delà de fournir la signification de base des différents principes. Cela me dérange vraiment. J'ai fait quelques jours de fouille et je n'ai pas encore trouvé de résumé satisfaisant.
La question de l'entretien était:
Si vous regardiez un projet .Net dont je vous ai dit que vous suiviez strictement les principes SOLID, que vous attendriez-vous à voir en termes de projet et de structure de code?
J'ai pataugé un peu, je n'ai pas vraiment répondu à la question, puis j'ai bombardé.
Comment aurais-je pu mieux gérer cette question?
S = principe de responsabilité unique
Je m'attends donc à voir une structure de dossiers/fichiers et une hiérarchie d'objets bien organisées. Chaque classe/élément de fonctionnalité doit être nommé de manière à ce que sa fonctionnalité soit très évidente et ne doit contenir que la logique pour effectuer cette tâche.
Si vous voyiez d'énormes classes de gestionnaires avec des milliers de lignes de code, ce serait le signe qu'une seule responsabilité n'était pas respectée.
O = principe ouvert/fermé
C'est essentiellement l'idée que de nouvelles fonctionnalités devraient être ajoutées à travers de nouvelles classes qui ont un minimum d'impact sur/nécessitent une modification des fonctionnalités existantes.
Je m'attendrais à voir beaucoup d'utilisation de l'héritage d'objet, du sous-typage, des interfaces et des classes abstraites pour séparer la conception d'un élément de fonctionnalité de l'implémentation réelle, permettant aux autres de venir et d'implémenter d'autres versions sans affecter la original.
L = principe de substitution de Liskov
Cela a à voir avec la possibilité de traiter les sous-types comme leur type parent. Cela sort de la boîte en C # si vous implémentez une hiérarchie d'objets hérités appropriée.
Je m'attendrais à voir du code traiter les objets communs comme leur type de base et appeler des méthodes sur les classes de base/abstraites plutôt que d'instancier et de travailler sur les sous-types eux-mêmes.
I = principe de séparation d'interface
Ceci est similaire à SRP. Fondamentalement, vous définissez des sous-ensembles de fonctionnalités plus petits en tant qu'interfaces et travaillez avec celles-ci pour garder votre système découplé (par exemple, un FileManager
pourrait avoir la responsabilité unique de traiter les E/S de fichiers, mais cela pourrait implémenter un IFileReader
et IFileWriter
qui contenaient les définitions de méthodes spécifiques pour la lecture et l'écriture des fichiers).
D = principe d'inversion de dépendance.
Encore une fois, cela concerne le maintien d'un système découplé. Peut-être seriez-vous à la recherche de l'utilisation d'une bibliothèque d'injection de dépendances .NET, utilisée dans la solution telle que Unity
ou Ninject
ou d'un système ServiceLocator tel que AutoFacServiceLocator
.
Beaucoup de petites classes et interfaces avec injection de dépendance partout. Probablement dans un grand projet, vous utiliseriez également un cadre IoC pour vous aider à construire et à gérer la durée de vie de tous ces petits objets. Voir https://stackoverflow.com/questions/21288/which-net-dependency-injection-frameworks-are-worth-looking-into
Notez qu'un grand projet .NET qui STRICTEMENT suit SOLID ne signifient pas nécessairement une bonne base de code pour travailler avec tout le monde. Selon qui était l'intervieweur, il/elle aurait peut-être voulu que vous montriez que vous comprenez ce que SOLID signifie et/ou vérifiez dans quelle mesure vous suivez dogmatiquement les principes de conception.
Vous voyez, pour être SOLIDE, vous devez suivre:
[~ # ~] s [~ # ~] principe de responsabilité ingle, vous aurez donc de nombreuses petites classes chacune d'elles ne faisant qu'une seule chose
[~ # ~] o [~ # ~] principe de fermeture du stylet, qui dans .NET est généralement implémenté avec une injection de dépendance, qui nécessite également le I et le D ci-dessous ...
[~ # ~] l [~ # ~] le principe de substitution iskov est probablement impossible à expliquer en c # avec une ligne. Heureusement, il existe d'autres questions à ce sujet, par exemple https://stackoverflow.com/questions/4428725/can-you-explain-liskov-substitution-principle-with-a-good-c-sharp-example
[~ # ~] i [~ # ~] Le principe de ségrégation d'interface fonctionne en tandem avec le principe ouvert-fermé. S'il est suivi littéralement, cela signifierait préférer un grand nombre de très petites interfaces plutôt que quelques "grandes" interfaces
[~ # ~] d [~ # ~] principe d'inversion d'épendence les classes de haut niveau ne doivent pas dépendre de classes de bas niveau, les deux doivent dépendre d'abstractions .
Certaines choses de base que je m'attendrais à voir dans la base de code d'une boutique qui épousait SOLID dans leur travail quotidien:
Beaucoup de modèles d'adaptateurs et composites - je m'attendrais à ce que beaucoup de modèles d'adaptateurs (une classe implémentant une interface en "passant" à la fonctionnalité d'une interface différente) rationalisent le branchement d'une dépendance développée dans un seul but en légèrement différents endroits où sa fonctionnalité est également nécessaire. Les mises à jour aussi simples que le remplacement d'un enregistreur de console par un enregistreur de fichiers violeront LSP/ISP/DIP si l'interface est mise à jour pour exposer un moyen de spécifier le nom de fichier à utiliser; à la place, la classe de l'enregistreur de fichiers exposera les membres supplémentaires, puis un adaptateur fera ressembler l'enregistreur de fichiers à un enregistreur de console en masquant les nouveaux éléments, de sorte que seul l'objet qui capture tout cela ensemble doit connaître la différence.
De même, lorsqu'une classe doit ajouter une dépendance d'une interface similaire à une interface existante, pour éviter de changer l'objet (OCP), la réponse habituelle est d'implémenter un modèle Composite/Strategy (une classe implémentant l'interface de dépendance et consommant plusieurs autres implémentations de cette interface, avec des quantités variables de logique permettant à la classe de passer un appel via une, certaines ou la totalité des implémentations).
Distrayez-les avec discussion de Jon Skeet sur la façon dont le 'O' dans SOLID est "inutile et mal compris" et faites-les parler de la "variation protégée" d'Alistair Cockburn et Josh Bloch "conçoit l'héritage, ou l'interdit".
Bref résumé de l'article de Skeet (bien que je ne recommanderais pas de laisser tomber son nom sans lire le billet de blog original!):
Le PO a demandé: "Comment aurais-je pu mieux traiter cette question?" En tant qu'ingénieur principal qui mène une entrevue, je serais infiniment plus intéressé par un candidat qui peut parler intelligemment des avantages et des inconvénients des différents styles de conception de code que par quelqu'un qui peut créer une liste de points.
Une autre bonne réponse serait: "Eh bien, cela dépend de la façon dont ils l'ont compris. Si tout ce qu'ils savent est les mots à la mode SOLID, je m'attendrais à un abus d'héritage, à une utilisation excessive de l'injection de dépendance) frameworks, un million de petites interfaces dont aucune ne reflète le vocabulaire du domaine utilisé pour communiquer avec la gestion des produits ... "
Il y a probablement un certain nombre de façons de répondre à cette question dans des délais variables. Cependant, je pense que c'est plus dans le sens de "Savez-vous ce que SOLID signifie?") Donc, répondre à cette question se résume probablement à frapper les points et à l'expliquer en termes de projet.
Donc, vous vous attendez à voir ce qui suit:
C'est une excellente question, même si je pense que c'est une question d'entrevue difficile.
Les principes SOLID régissent vraiment les classes et les interfaces et la façon dont ils se sont reliés.
Cette question est vraiment celle qui a plus à voir avec les fichiers et pas nécessairement les classes.
Une brève observation ou réponse que je donnerais est que généralement vous verrez des fichiers qui ne contiennent qu'une interface, et souvent la convention est qu'ils commencent par un I majuscule. Au-delà de cela, je mentionnerais que les fichiers n'auraient pas de code dupliqué (en particulier dans un module, une application ou une bibliothèque), et que le code serait soigneusement partagé à travers certaines limites entre les modules, les applications ou les bibliothèques.
Robert Martin aborde ce sujet dans le domaine du C++ dans Conception d'applications C++ orientées objet à l'aide de la méthode Booch (voir les sections sur Cohésion, Fermeture et Réutilisation) et dans Clean Code .