J'aime SOLID et je fais de mon mieux pour l’utiliser et l’appliquer lorsque je me développe. Mais je ne peux pas m'empêcher de penser que l'approche SOLID transforme votre code en code 'framework' - le code que vous concevriez si vous créiez un framework ou une bibliothèque que d'autres développeurs pourraient utiliser.
J'ai généralement pratiqué 2 modes de programmation: créer plus ou moins exactement ce qui est demandé via les exigences et KISS (programmation typique), ou créer une logique, des services, etc. très génériques et réutilisables offrant la flexibilité dont d'autres développeurs peuvent avoir besoin (programmation par cadre). .
Si l'utilisateur veut vraiment juste qu'une application fasse des choses x et y, est-il logique de suivre SOLID et d'ajouter toute une série de points d'entrée d'abstraction, alors que vous ne savez même pas si c'est un problème valable pour commencer avec? Si vous ajoutez ces points d’abstraction d’entrée, répondez-vous réellement aux besoins des utilisateurs ou créez-vous un cadre au-dessus de votre cadre existant et de votre pile technologique pour faciliter les ajouts futurs? Dans quel cas servez-vous les intérêts du client ou du développeur?
C'est quelque chose qui semble commun dans le monde de Java Enterprise, où vous avez l'impression de concevoir votre propre framework au-dessus de J2EE ou de Spring pour qu'il soit un meilleur UX pour le développeur, au lieu de vous concentrer sur l'UX pour l'utilisateur?
la source
Réponses:
Votre observation est correcte, les principes SOLID sont conçus à mon humble avis avec des bibliothèques réutilisables ou un code de framework à l’esprit. Lorsque vous les suivez tous aveuglément, sans vous demander si cela a un sens ou non, vous risquez de trop généraliser et d'investir beaucoup plus d'efforts dans votre système que probablement nécessaire.
C'est un compromis, et il faut un peu d'expérience pour prendre les bonnes décisions quant au moment opportun pour généraliser ou non. Une approche possible consiste à s'en tenir au principe de YAGNI - ne rendez pas votre code SOLID "au cas où" - ou, pour reprendre vos mots: ne le faites pas.
au lieu de cela, fournissez la flexibilité dont les autres développeurs ont réellement besoin dès qu'ils en ont besoin , mais pas avant.
Ainsi, chaque fois que vous avez une fonction ou une classe dans votre code, vous ne savez pas si elle pourra être réutilisée, ne la placez pas dans votre cadre pour le moment. Attendez que vous ayez un cas réel de réutilisation et que le refactor devienne "assez solide pour ce cas". N'implémentez pas davantage de configurabilité (en suivant l'OCP), ni de points d'abstraction d'entrée (à l'aide du DIP) dans une classe dont vous avez réellement besoin pour le cas de réutilisation réel. Ajoutez la flexibilité suivante lorsque la prochaine exigence de réutilisation est réellement présente.
Bien sûr, cette façon de travailler nécessitera toujours une refonte de la base de code existante. C'est pourquoi les tests automatiques sont importants ici. Donc, rendre votre code suffisamment SOLIDE dès le début pour qu'il soit testable par unité n'est pas une perte de temps, et cela ne contredit pas YAGNI. Les tests automatiques constituent un cas valable de "réutilisation de code", car le code en jeu est utilisé à partir du code de production ainsi que des tests. Mais gardez à l'esprit, ajoutez simplement la flexibilité dont vous avez réellement besoin pour que les tests fonctionnent, ni moins, ni plus.
C'est en fait une vieille sagesse. Il y a longtemps avant que le terme SOLID devenu populaire, quelqu'un m'a dit avant d' essayer d'écrire à nouveau le code utilisable, nous devrions écrire utilisables code. Et je pense toujours que c'est une bonne recommandation.
la source
D'après mon expérience, lorsque vous écrivez une application, vous avez trois choix:
Dans le premier cas, il est courant de se retrouver avec un code étroitement couplé qui manque de tests unitaires. Bien sûr, c'est rapide à écrire, mais difficile à tester. Et c'est une douleur royale juste de changer plus tard lorsque les exigences changent.
Dans le second cas, on passe beaucoup de temps à essayer d’anticiper les besoins futurs. Et trop souvent, ces exigences futures ne se matérialisent jamais. Cela semble le scénario que vous décrivez. C'est un gaspillage d'efforts la plupart du temps et donne lieu à un code inutilement complexe qu'il est encore difficile de modifier lorsqu'une exigence inattendue se présente.
Le dernier cas est celui à viser à mon avis. Utilisez TDD ou des techniques similaires pour tester le code au fur et à mesure et vous obtiendrez un code faiblement couplé, facile à modifier mais rapide à écrire. Et le fait est que, ce faisant, vous suivez naturellement bon nombre des principes SOLID: petites classes et fonctions; interfaces et dépendances injectées. Et Mme Liskov est généralement satisfaite aussi, car les classes simples à responsabilité unique ne sont pas gênées par son principe de substitution.
Le seul aspect de SOLID qui ne s'applique pas vraiment ici est le principe d'ouverture / fermeture. Pour les bibliothèques et les frameworks, c'est important. Pour une application autonome, pas tellement. En réalité, c’est un cas d’écriture de code qui suit " SLID ": facile à écrire (et à lire), facile à tester et facile à maintenir.
la source
Votre perspective peut être faussée par votre expérience personnelle. Il s’agit d’une pente glissante de faits individuellement corrects, mais la déduction qui en résulte ne l’est pas, même si elle semble correcte au premier abord.
Cela signifie que lorsque vous interagissez avec des frameworks et des bibliothèques plus petites, le code de bonne pratique avec lequel vous interagissez se trouve plus souvent dans les frameworks plus grands.
Cette erreur est très courante, par exemple chaque médecin chez qui j'ai été traité était arrogant. Par conséquent, je conclus que tous les médecins sont arrogants. Ces sophismes souffrent toujours de faire une inférence générale basée sur des expériences personnelles.
Dans votre cas, il est possible que vous ayez principalement expérimenté les bonnes pratiques dans des cadres plus grands et non dans des bibliothèques plus petites. Votre observation personnelle n’est pas fausse, mais il s’agit d’une preuve anecdotique et non universellement applicable.
Vous confirmez un peu cela ici. Pensez à ce qu'est un cadre. Ce n'est pas une application. C'est un "modèle" généralisé que d'autres peuvent utiliser pour faire toutes sortes d'applications. Logiquement, cela signifie qu'un cadre est construit dans une logique beaucoup plus abstraite pour pouvoir être utilisé par tout le monde.
Les constructeurs de framework sont incapables de prendre des raccourcis, car ils ne savent même pas quelles sont les exigences des applications suivantes. Construire un framework les incite naturellement à rendre leur code utilisable par d'autres.
Les concepteurs d'applications ont toutefois la possibilité de compromettre l'efficacité logique, car ils se concentrent sur la fourniture d'un produit. Leur principal objectif n'est pas le fonctionnement du code, mais plutôt l'expérience de l'utilisateur.
Pour un framework, l'utilisateur final est un autre développeur, qui interagira avec votre code. La qualité de votre code est importante pour l'utilisateur final.
Pour une application, l'utilisateur final est un non-développeur, qui n'interagira pas avec votre code. La qualité de votre code n’a aucune importance pour eux.
C'est précisément pourquoi les architectes d'une équipe de développement agissent souvent en tant que responsables de l'application des bonnes pratiques. La livraison du produit leur est très facile, ce qui signifie qu'ils ont tendance à regarder le code de manière objective, plutôt que de se concentrer sur la livraison de l'application elle-même.
C’est un point intéressant, et c’est (selon mon expérience) la principale raison pour laquelle les gens essaient encore de justifier d’éviter les bonnes pratiques.
Pour résumer les points ci-dessous: Ignorer les bonnes pratiques ne peut être justifié que si vos exigences (telles que connues à ce jour) sont immuables et qu’il n’y aura jamais de modification / ajout à la base de code. Alerte spoiler: C'est rarement le cas.
Par exemple, lorsque j'écris une application console 5 minutes pour traiter un fichier particulier, je n'utilise pas les bonnes pratiques. Parce que je vais seulement utiliser l'application aujourd'hui, et qu'elle n'aura pas besoin d'être mise à jour à l'avenir (il serait plus facile d'écrire une application différente si j'en avais besoin une de plus).
Supposons que vous puissiez compiler correctement une application en 4 semaines et que vous puissiez le créer correctement en 6 semaines. À première vue, la construction bâclée semble meilleure. Le client reçoit leur application plus rapidement et l'entreprise doit consacrer moins de temps aux salaires des développeurs. Gagner / Gagner, non?
Cependant, cette décision est prise sans réflexion. En raison de la qualité de la base de code, il faut compter 2 semaines pour apporter des modifications majeures à celui qui a été construit de façon médiocre, tandis qu’il faut une semaine pour les mêmes modifications. Beaucoup de ces changements pourraient se produire à l’avenir.
En outre, les modifications ont tendance à nécessiter de manière inattendue plus de travail que prévu au départ dans des bases de code mal construites, ce qui a pour effet de forcer votre temps de développement à trois semaines au lieu de deux.
Et puis, il y a aussi la tendance à perdre du temps à chasser les insectes. C'est souvent le cas dans les projets où la journalisation a été ignorée en raison de contraintes de temps ou de réticence totale à la mettre en œuvre car vous travaillez de manière distraite en supposant que le produit final fonctionnera comme prévu.
Cela n'a même pas besoin d'être une mise à jour majeure. Chez mon employeur actuel, j'ai vu plusieurs projets construits rapidement et mal, et lorsque le plus petit bogue / changement devait être corrigé en raison d'une mauvaise communication dans les exigences, cela a entraîné une réaction en chaîne du besoin de refactoriser module après module. . Certains de ces projets ont fini par s'effondrer (et laisser derrière eux un gâchis incommenable) avant même de publier leur première version.
Les décisions de raccourci (programmation rapide et imprécise) ne sont utiles que si vous pouvez garantir de manière concluante que les exigences sont exactement correctes et qu’elles n’auront jamais besoin de changer. D'après mon expérience, je n'ai jamais rencontré de projet où c'est vrai.
Investir plus de temps dans les bonnes pratiques, c'est investir dans l'avenir. Les bugs et modifications futurs seront tellement plus faciles lorsque la base de code existante est construite sur de bonnes pratiques. Il versera déjà des dividendes après seulement deux ou trois modifications.
la source
Comment SOLID transforme-t-il un code simple en code-cadre? Je ne suis certes pas un fan de SOLID, mais ce que vous voulez dire n’est pas vraiment évident.
J'avoue que je ne pense pas en termes solides moi-même, car je suis passé par les écoles de programmation Gang of Four et Josh Bloch , et non par l'école Bob Martin. Mais je pense vraiment que si vous pensez “SOLID” = “ajouter plus de couches à la pile de technologies”, vous la lisez mal.
PS Ne vendez pas les avantages de “meilleur UX pour le développeur”. Le code passe le plus clair de son temps en maintenance. Un développeur, c'est vous .
la source
class A{ int X; int Y; } class A_setX{ f(A a, int N) { a.X = N; }} class A_getX{ int f(A a) { return X; }} class A_setY ... etc.
Je pense que vous envisagez cela d'un point de vue trop méta avec votre revendication d'usine. L'initialisation n'est pas un aspect du problème de domaine.