En tant que programmeur, je me trouve dans le dilemme où je veux que mon programme soit aussi abstrait et général que possible.
Cela me permettrait généralement de réutiliser mon code et d’avoir une solution plus générale à un problème qui pourrait (ou ne pourrait pas) se reproduire.
Ensuite, cette voix dans ma tête dit, il suffit de résoudre le problème factice, c'est aussi simple que cela! Pourquoi dépenser plus de temps que nécessaire?
Nous avons tous été confrontés à cette question, où l’abstraction est sur votre épaule droite et où Résoudre-le-stupide est assis à gauche.
Lequel à écouter et à quelle fréquence? Quelle est votre stratégie pour cela? Devez-vous tout faire abstraction?
time-management
problem-solving
abstraction
Bryan Harrington
la source
la source
Réponses:
Ne faites jamais abstraction tant que vous ne le devez pas.
En Java, par exemple, vous devez utiliser des interfaces. Ils sont une abstraction.
En Python, vous n'avez pas d'interface, vous avez Duck Typing, et vous n'avez pas besoin d'abstraction. Donc vous ne le faites pas.
Ne résumez pas avant de l'avoir écrit trois fois.
Une fois est - bien - une fois. Il suffit de le résoudre et de passer à autre chose.
Deux fois, cela indique qu'il peut y avoir un motif ici. Ou peut-être pas. Ce peut être juste une coïncidence.
Trois fois est le début d'un motif. Maintenant, cela transcende les coïncidences. Vous pouvez maintenant résumer avec succès.
Non.
En effet, vous ne devriez jamais rien abstrait tant que vous n’avez pas la preuve absolue que vous faites le bon type d’abstraction. Sans la "règle des trois répétitions", vous écrivez des choses inutilement abstraites d'une manière inutile.
C'est une hypothèse qui est souvent fausse. L'abstraction peut ne pas aider du tout. Cela peut être mal fait. Par conséquent, ne le faites pas avant que vous ne deviez.
la source
Ah, YAGNI. Le concept de programmation le plus abusé.
Il y a une différence entre rendre votre code générique et faire un travail supplémentaire. Devez-vous passer plus de temps à faire votre code faiblement couplé et facilement adapté à faire d'autres choses? Absolument. Devriez-vous passer du temps à mettre en œuvre des fonctionnalités inutiles? Non. Devriez-vous passer du temps à faire en sorte que votre code fonctionne avec un autre code qui n'a pas encore été implémenté? Non.
Comme dit le proverbe "Faites la chose la plus simple qui puisse fonctionner". La chose est que les gens confondent toujours simple avec facile . La simplicité prend du travail. Mais cela en vaut la peine.
Pouvez-vous aller à la mer? Bien sûr. Mais, d’après mon expérience, peu d’entreprises ont besoin d’une attitude plus ambitieuse qu’elles ne l’ont déjà fait. La plupart des entreprises ont besoin de plus de personnes qui réfléchissent à l'avance. Sinon, ils finissent toujours par avoir un problème autonome où personne n'a le temps de rien, tout le monde est toujours pressé et personne ne fait rien.
Pensez-y de cette façon: il y a de bonnes chances que votre code soit utilisé à nouveau. Mais vous n'allez pas prédire correctement de cette façon. Alors, faites en sorte que votre code soit propre, abstrait et faiblement couplé. Mais n'essayez pas de faire fonctionner votre code avec des fonctionnalités spécifiques qui n'existent pas encore. Même si vous savez que cela existera dans le futur.
la source
simple
eteasy
!Un syllogisme:
La généralité est chère.
Vous dépensez l'argent des autres.
Par conséquent, le coût de la généralité doit être justifié auprès des parties prenantes.
Demandez-vous si vous résolvez réellement le problème plus général afin d'économiser de l'argent aux parties prenantes ultérieurement, ou si vous trouvez simplement un défi intellectuel de trouver une solution inutilement générale.
Si la généralité est jugée souhaitable, elle devrait être conçue et testée comme toute autre fonctionnalité . Si vous ne parvenez pas à écrire des tests démontrant que la généralité que vous avez implémentée résout un problème requis par la spécification, évitez de le faire! Une fonctionnalité qui ne répond à aucun critère de conception et ne peut pas être testée est une fonctionnalité sur laquelle personne ne peut compter.
Et enfin, il n’existe pas de "aussi général que possible". Supposons que vous écriviez un logiciel en C #, juste pour les besoins de l’argumentation. Allez-vous faire en sorte que chaque classe implémente une interface? Chaque classe une classe de base abstraite avec chaque méthode une méthode abstraite? C'est assez général, mais c'est loin d'être "aussi général que possible". Cela permet simplement aux gens de changer la mise en œuvre de n'importe quelle méthode en sous-classant. Et s'ils veulent changer l'implémentation d'une méthode sans sous-classement? Vous pouvez faire de chaque méthode une propriété de type délégué, avec un séparateur afin que les utilisateurs puissent changer chaque méthode. Mais que faire si quelqu'un veut ajouter plus de méthodes? Maintenant, chaque objet doit être extensible.
À ce stade, vous devriez abandonner le langage C # et adopter JavaScript. Mais vous n'avez toujours pas été assez général; Et si quelqu'un veut changer le fonctionnement de la recherche de membre pour cet objet en particulier? Peut-être devriez-vous tout écrire en Python à la place.
Augmenter la généralité signifie souvent abandonner la prévisibilité et augmenter considérablement les coûts de test pour s'assurer que la généralité implémentée réussit réellement à répondre à un réel besoin de l'utilisateur . Ces coûts sont-ils justifiés par leurs avantages pour les parties prenantes qui les paient? Peut-être qu'ils sont; c'est à vous de discuter avec les intervenants. Mes parties prenantes ne veulent pas du tout que j'abandonne le typage statique à la recherche d'un niveau de généralité tout à fait inutile et extrêmement coûteux, mais peut-être le vôtre.
la source
Soyons clairs: généraliser quelque chose et mettre en œuvre une abstraction sont deux choses complètement différentes.
Par exemple, considérons une fonction qui copie la mémoire.
La fonction est une abstraction qui masque la façon dont les 4 octets sont copiés.
int copy4Bytes (char * pSrc, char * pDest)
Une généralisation ferait que cette fonction copie un nombre quelconque d’octets.
int copyBytes (char * pSrc, char * pDest, int numBytesToCopy)
L'abstraction se prête à la réutilisation, alors que la généralisation la rend utile dans de nombreux cas.
Plus spécifiquement liée à votre question, l'abstraction n'est pas seulement utile du point de vue de la réutilisation du code. Souvent, si cela est fait correctement, votre code sera plus lisible et maintenable. À l'aide de l'exemple ci-dessus, qu'est-ce qui est plus facile à lire et à comprendre si vous parcourez du code copyBytes () ou une boucle for itérant un tableau déplaçant des données, index par index? L'abstraction peut fournir une sorte de documentation personnelle qui, à mon avis, rend le code plus facile à utiliser.
En règle générale, si je parviens à trouver un bon nom de fonction décrivant exactement ce que je souhaite qu'un morceau de code fasse, je lui écris une fonction, que je pense ou non que je l'utiliserai à nouveau.
la source
Une bonne règle générale pour ce genre de chose est Zéro, Un, Infini. Autrement dit, si vous avez besoin de ce que vous écrivez plus d'une fois, vous pouvez en déduire que vous en aurez besoin encore plus et que vous généralisez. Cette règle implique que vous ne vous souciez pas de l’abstraction la première fois que vous écrivez quelque chose.
Une autre bonne raison de cette règle est que la première fois que vous écrivez le code, vous ne saurez pas forcément quoi résumer car vous n’avez qu’un seul exemple. La loi de Murphy implique que si vous écrivez un code abstrait la première fois, le deuxième exemple présentera des différences que vous n'aviez pas anticipées.
la source
Eric Lippert souligne trois choses qui, à mon avis, s'appliquent dans son article sur la vérification future d'un dessin . Je pense que si vous le suivez, vous serez en forme.
la source
Cela dépend de la raison pour laquelle vous codez, du but de votre projet. Si la valeur de votre code est qu'elle résout un problème concret, vous souhaitez que cela soit fait et que vous passiez au problème suivant. S'il existe des solutions simples et rapides pour simplifier la tâche des futurs utilisateurs du code (y compris vous-même), apportez-nous à tout prix des adaptations raisonnables.
D'autre part, il existe des cas où le code que vous écrivez a un but plus générique. Par exemple, lorsque vous écrivez une bibliothèque pour d’autres programmeurs, elle l’utilisera dans une grande variété d’applications. Les utilisateurs potentiels sont inconnus et vous ne pouvez pas leur demander exactement ce qu'ils veulent. Mais vous voulez rendre votre bibliothèque largement utile. Dans de tels cas, je passerais plus de temps à essayer de soutenir la solution générale.
la source
Je suis un grand fan du principe KISS.
Je me concentre sur ce que l’on me demande de faire et non sur la meilleure solution. J'ai dû abandonner le perfectionnisme (et le TOC), car cela me rendait malheureux.
la source
Je ne suis pas d'accord avec "résoudre le stupide" , je pense qu'il peut être plus "résoudre le malin" .
Quoi de plus intelligent:
Le choix par défaut devrait être le deuxième. Sauf si vous pouvez démontrer un besoin de génération.
La solution généralisée ne devrait être que lorsque vous savez que c'est quelque chose qui sera utilisé dans plusieurs projets / cas différents.
Habituellement, je trouve que les solutions généralisées sont mieux utilisées en tant que "code de bibliothèque principal".
la source