Je comprends ce que SOLID est censé accomplir et je l'utilise régulièrement dans des situations où la modularité est importante et ses objectifs sont clairement utiles. Cependant, deux choses m'empêchent de l'appliquer de manière cohérente dans ma base de code:
Je veux éviter une abstraction prématurée. D'après mon expérience, tracer des lignes d'abstraction sans cas d'utilisation concrets (du genre de ceux qui existent actuellement ou dans un avenir prévisible ) conduit à les tracer aux mauvais endroits. Lorsque j'essaie de modifier un tel code, les lignes d'abstraction gênent plutôt que d'aider. Par conséquent, j'ai tendance à me tromper en évitant de tracer des lignes d'abstraction jusqu'à ce que j'aie une bonne idée de l'endroit où elles seraient utiles.
Je trouve difficile de justifier l'augmentation de la modularité pour elle-même si cela rend mon code plus verbeux, plus difficile à comprendre, etc. et n'élimine aucune duplication. Je trouve que le code procédural ou objet Dieu simple et étroitement couplé est parfois plus facile à comprendre qu'un code ravioli très bien factorisé car le flux est simple et linéaire. C'est aussi beaucoup plus facile à écrire.
D'un autre côté, cet état d'esprit mène souvent à des objets divins. Je refaçonne généralement ces derniers de façon conservatrice, en ajoutant des lignes d'abstraction claires uniquement lorsque je vois des modèles clairs émerger. Qu'est-ce qui ne va pas avec les objets de Dieu et le code étroitement couplé si vous n'avez pas clairement besoin de plus de modularité, n'avez pas de duplication significative et le code est lisible?
EDIT: En ce qui concerne les principes SOLID individuels, je voulais souligner que la substitution de Liskov est à mon humble avis une formalisation du bon sens et devrait être appliquée partout, car les abstractions n'ont aucun sens si ce n'est pas le cas. En outre, chaque classe devrait avoir une responsabilité unique à un certain niveau d'abstraction, bien qu'il puisse s'agir d'un niveau très élevé avec les détails d'implémentation regroupés dans une énorme classe de 2 000 lignes. Fondamentalement, vos abstractions doivent avoir un sens là où vous choisissez d'abstraire. Les principes que je remets en question dans les cas où la modularité n'est pas clairement utile sont la fermeture ouverte, la ségrégation d'interfaces et surtout l'inversion de dépendance, car il s'agit de modularité, et pas seulement d'avoir des abstractions sensées.
la source
Réponses:
Voici des principes simples que vous pouvez appliquer pour vous aider à comprendre comment équilibrer la conception de votre système:
S
in SOLID. Vous pouvez avoir un très grand objet en termes de nombre de méthodes ou de quantité de données tout en respectant ce principe. Prenons par exemple l'objet Ruby String. Cet objet a plus de méthodes que vous ne pouvez le faire, mais il n'a qu'une seule responsabilité: tenir une chaîne de texte pour l'application. Dès que votre objet commence à assumer une nouvelle responsabilité, réfléchissez bien à cela. Le problème de maintenance est de vous demander "où puis-je m'attendre à trouver ce code si j'ai eu des problèmes avec lui plus tard?"En substance, vous essayez de faire ce que vous pouvez pour ne pas vous tirer une balle dans le pied quand vient le temps de maintenir le logiciel. Si vos gros objets sont des abstractions raisonnables, il n'y a pas de raison de les diviser simplement parce que quelqu'un a proposé une métrique qui dit qu'une classe ne doit pas être plus de X lignes / méthodes / propriétés / etc. Si quoi que ce soit, ce sont des lignes directrices et non des règles dures et rapides.
la source
Je pense que vous avez pour la plupart répondu à votre propre question. SOLID est un ensemble de directives sur la façon de refactoriser votre code, lorsque les concepts doivent être remontés à des niveaux d'abstractions.
Comme toutes les autres disciplines créatives, il n'y a pas d'absolu - juste des compromis. Plus vous le faites, plus il devient «facile» de décider quand est suffisant pour votre domaine ou vos besoins actuels.
Cela dit - l'abstrait est au cœur du développement de logiciels - donc s'il n'y a pas de bonne raison de ne pas le faire, alors la pratique vous rendra meilleur et vous aurez une intuition pour les compromis. Privilégiez-la donc à moins qu'elle ne devienne préjudiciable.
la source
C'est partiellement vrai et partiellement faux.
Mauvais
Ce n'est pas une raison pour empêcher que l'on applique les principes OO / SOLID. Cela empêche seulement de les appliquer trop tôt.
Lequel est...
Droite
Ne refactorisez pas le code tant qu'il n'est pas refactorisable; lorsque les exigences sont remplies. Ou, lorsque les «cas d'utilisation» sont tous là comme vous le dites.
Lorsque vous travaillez seul ou en silo
Le problème de ne pas faire d'OO n'est pas immédiatement évident. Après avoir écrit la première version d'un programme:
3/4 de ces bonnes choses meurent rapidement en peu de temps.
Enfin, vous reconnaissez que vous avez des objets divins (objets avec de nombreuses fonctions), vous reconnaissez donc des fonctions individuelles. Encapsuler. Mettez-les dans des dossiers. Utilisez des interfaces de temps en temps. Faites-vous plaisir pour l'entretien à long terme. D'autant plus que les méthodes non refactorisées ont tendance à gonfler à l'infini et à devenir des méthodes divines.
En équipe
Les problèmes de non-OO sont immédiatement perceptibles. Un bon code OO est au moins quelque peu auto-documenté et lisible. Surtout lorsqu'il est combiné avec un bon code vert. De plus, le manque de structure rend difficile la séparation des tâches et l'intégration beaucoup plus complexe.
la source
Il n'y a rien de mal avec les gros objets et le code étroitement couplé lorsqu'ils sont appropriés et lorsque rien de mieux conçu n'est requis . Ceci est juste une autre manifestation d' une règle d'or qui se transforme en dogme.
Le couplage lâche et les petits objets simples ont tendance à fournir des avantages spécifiques dans de nombreux cas courants, c'est donc une bonne idée de les utiliser, en général. Le problème réside dans les personnes qui ne comprennent pas la raison d'être des principes qui essaient aveuglément de les appliquer universellement, même là où elles ne s'appliquent pas.
la source
J'ai tendance à aborder cela plus du point de vue de You Ain't Gonna Need It. Ce message se concentrera sur un élément en particulier: l'héritage.
Dans ma conception initiale, je crée une hiérarchie de choses dont je sais qu'il y en aura deux ou plus. Il est probable qu'ils auront besoin d'une grande partie du même code, il vaut donc la peine de le concevoir dès le départ. Une fois que le code initial est en place et que j'ai besoin d'ajouter plus de fonctionnalités / fonctionnalités, je regarde ce que j'ai et je pense: "Est-ce que quelque chose que j'ai déjà implémente la même fonction ou une fonction similaire?" Si c'est le cas, c'est très probablement une nouvelle abstraction qui demande à être publiée.
Un bon exemple de ceci est un framework MVC. À partir de zéro, vous créez une grande page avec un code derrière. Mais alors vous voulez ajouter une autre page. Cependant, le code derrière implémente déjà une grande partie de la logique requise par la nouvelle page. Donc, vous résumez une
Controller
classe / interface qui implémentera la logique spécifique à cette page, laissant les trucs communs dans le code "dieu" d'origine.la source
Si VOUS savez en tant que développeur quand NE PAS appliquer les principes en raison de l'avenir du code, alors c'est bon pour vous.
J'espère que SOLID reste dans votre esprit pour savoir quand les choses doivent être abstraites pour éviter la complexité et améliorer la qualité du code s'il commence à se dégrader dans les valeurs que vous avez indiquées.
Mais plus important encore, considérez que la majorité des développeurs font le travail de jour et ne s'en soucient pas autant que vous. Si vous avez initialement défini des méthodes de longue durée, quels sont les autres développeurs qui entrent dans le code vont penser quand ils viennent pour le maintenir ou l'étendre? ... Oui, vous l'avez deviné, BIGGER fonctions, LONGER running classes et PLUS les objets divins, et ils n'ont pas les principes en tête pour pouvoir attraper le code croissant et l'encapsuler correctement. Votre code "lisible" devient maintenant un gâchis pourri.
Vous pouvez donc affirmer qu'un mauvais développeur va le faire malgré tout, mais au moins vous avez un meilleur avantage si les choses restent simples et bien organisées dès le départ.
Cela ne me dérange pas particulièrement une "carte de registre" globale ou un "objet divin" si elles sont simples. Cela dépend vraiment de la conception du système, parfois vous pouvez vous en tirer et rester simple, parfois vous ne pouvez pas.
N'oublions pas non plus que les grandes fonctions et les objets divins peuvent s'avérer extrêmement difficiles à tester. Si vous voulez rester agile et vous sentir "en sécurité" lors de la refactorisation et de la modification du code, vous avez besoin de tests. Les tests sont difficiles à écrire lorsque les fonctions ou les objets font beaucoup de choses.
la source
Le problème avec un objet divin est que vous pouvez généralement le briser en morceaux de manière rentable. Par définition, il fait plus d'une chose. Le but de le diviser en classes plus petites est de faire en sorte que vous puissiez avoir une classe qui fait bien une chose (et vous ne devriez pas non plus étirer la définition de "une chose"). Cela signifie que, pour une classe donnée, une fois que vous savez la seule chose qu'elle est censée faire, vous devriez être capable de la lire assez facilement et de dire si oui ou non elle fait correctement cette chose.
Je pense qu'il y a une telle chose comme avoir trop de modularité et trop de flexibilité, mais c'est plus un problème de sur-conception et de sur-ingénierie, où vous tenez compte d'exigences que vous ne savez même pas que le client veut. De nombreuses idées de conception visent à faciliter le contrôle des modifications de la façon dont le code est exécuté, mais si personne ne s'attend à ce changement, il est inutile d'inclure la flexibilité.
la source
Queryer
qui optimise les requêtes de base de données, interroge le SGBDR et analyse les résultats en objets à renvoyer. S'il n'y a qu'une seule façon de le faire dans votre application et qu'elleQueryer
est hermétiquement scellée et encapsule cette façon, alors cela ne fait qu'une chose. S'il y a plusieurs façons de le faire et que quelqu'un peut se soucier des détails d'une partie, il fait plusieurs choses et vous voudrez peut-être le diviser.J'utilise une directive plus simple: si vous pouvez écrire des tests unitaires pour cela, et ne pas avoir de duplication de code, c'est assez abstrait. De plus, vous êtes en bonne position pour une refactorisation ultérieure.
Au-delà de cela, vous devez garder SOLID à l'esprit, mais plus comme ligne directrice que comme règle.
la source
Si l'application est suffisamment petite, tout est maintenable. Dans les applications plus grandes, les objets divins deviennent rapidement un problème. Finalement, l'implémentation d'une nouvelle fonctionnalité nécessite de modifier 17 objets divins. Les gens ne réussissent pas très bien en suivant les procédures en 17 étapes. Les objets de Dieu sont constamment modifiés par plusieurs développeurs, et ces changements doivent être fusionnés à plusieurs reprises. Tu ne veux pas y aller.
la source
Je partage vos préoccupations concernant l'abstraction excessive et inappropriée, mais je ne suis pas nécessairement aussi préoccupé par l'abstraction prématurée.
Cela ressemble probablement à une contradiction, mais l'utilisation d'une abstraction est peu susceptible de causer un problème tant que vous ne vous engagez pas trop tôt - c'est-à-dire tant que vous êtes disposé et capable de refactoriser si nécessaire.
Cela implique un certain degré de prototypage, d'expérimentation et de retour en arrière dans le code.
Cela dit, il n'y a pas de règle déterministe simple à suivre pour résoudre tous les problèmes. L'expérience compte beaucoup, mais vous devez acquérir cette expérience en faisant des erreurs. Et il y a toujours plus d'erreurs à faire que les erreurs précédentes ne vous auront pas préparé.
Néanmoins, traitez les principes du manuel comme un point de départ, puis apprenez à programmer beaucoup et voyez comment ces principes fonctionnent. S'il était possible de donner des directives meilleures, plus précises et plus fiables que des choses comme SOLID, quelqu'un l'aurait probablement fait maintenant - et même s'ils l'avaient fait, ces principes améliorés seraient toujours imparfaits et les gens se poseraient des questions sur les limites de ces .
Bon travail aussi - si quelqu'un pouvait fournir un algorithme clair et déterministe pour concevoir et coder des programmes, il n'y aurait qu'un seul programme à écrire pour les humains - le programme qui écrirait automatiquement tous les futurs programmes sans intervention humaine.
la source
Tracer les lignes d'abstraction appropriées est quelque chose que l'on tire de l'expérience. Vous ferez des erreurs en le faisant, c'est indéniable.
SOLID est une forme concentrée de cette expérience qui vous est offerte par des personnes qui ont acquis cette expérience à la dure. Vous l'avez pratiquement cloué lorsque vous dites que vous vous abstiendriez de créer une abstraction avant d'en voir le besoin. Eh bien, l'expérience SOLID vous aidera à résoudre des problèmes que vous n'avez jamais connus . Mais croyez-moi ... il y en a de très réels.
SOLID concerne la modularité, la modularité concerne la maintenabilité, et la maintenabilité consiste à vous permettre de garder un horaire de travail humain une fois que le logiciel atteint la production et que le client commence à remarquer des bogues que vous n'avez pas.
Le plus grand avantage de la modularité est la testabilité. Plus votre système est modulaire, plus il est facile à tester et plus vite vous pouvez construire des fondations solides pour faire face aux aspects les plus difficiles de votre problème. Ainsi, votre problème peut ne pas exiger cette modularité, mais le fait qu'il vous permettra de créer un meilleur code de test plus rapidement est une évidence pour viser la modularité.
Être agile, c'est essayer de trouver l'équilibre délicat entre l'expédition rapide et la bonne qualité. Agile ne signifie pas que nous devons faire des économies, en fait, les projets agiles les plus réussis avec lesquels j'ai été impliqué sont ceux qui ont prêté une attention particulière à ces directives.
la source
Un point important qui semble avoir été négligé est que SOLID est pratiqué avec TDD. TDD a tendance à mettre en évidence ce qui est "approprié" dans votre contexte particulier et à aider à atténuer une grande partie de l'équivoque qui semble être dans les conseils.
la source