web-dev-qa-db-fra.com

Trop d'abstraction peut-elle être mauvaise?

En tant que programmeurs, je pense que notre objectif est de fournir de bonnes abstractions sur le modèle de domaine et la logique métier donnés. Mais où cette abstraction devrait-elle s'arrêter? Comment faire compromis entre l'abstraction et tous ses avantages (flexibilité, facilité de changement, etc.) et facilité de compréhension du code et tous ses avantages.

Je crois que j'ai tendance à écrire du code trop abstrait et je ne sais pas à quel point c'est bon; J'ai souvent tendance à l'écrire comme s'il s'agissait d'une sorte de micro-framework, qui se compose de deux parties:

  1. Micro-modules qui sont reliés dans le micro-cadre: ces modules sont faciles à comprendre, à développer et à entretenir comme des unités uniques. Ce code représente essentiellement le code qui fait réellement les choses fonctionnelles, décrites dans les exigences.
  2. Code de connexion; maintenant ici, je crois que se pose le problème. Ce code a tendance à être compliqué car il est parfois très abstrait et difficile à comprendre au début; cela tient au fait qu'il ne s'agit que d'une pure abstraction, la base en réalité et la logique métier étant réalisées dans le code présenté 1; pour cette raison, ce code ne devrait pas être modifié une fois testé.

Est-ce une bonne approche de programmation? Que cela, ayant du code changeant très fragmenté dans de nombreux modules et très facile à comprendre et du code non changeant très complexe du POV d'abstraction? Tout le code devrait-il être uniformément complexe (c'est-à-dire le code 1 plus complexe et interconnecté et le code 2 plus simple) afin que quiconque le parcoure puisse le comprendre dans un délai raisonnable mais que le changement coûte cher ou que la solution présentée ci-dessus soit bonne, où "changer le code" est très facile à comprendre, déboguer, changer et "lier le code" est un peu difficile.

Remarque: il ne s'agit pas de lisibilité du code! Le code à 1 et 2 sont lisibles, mais le code à 2 est livré avec des abstractions plus complexes tandis que le code 1 est livré avec des abstractions simples.

49
m3th0dman

Les tout premiers mots de TC++ PL4:

Tous les problèmes en informatique peuvent être résolus par un autre niveau d'indirection, à l'exception du problème de trop de couches d'indirection. - David J. Wheeler

(David Wheeler était mon conseiller de thèse. La citation sans la dernière ligne importante est parfois appelée "La première loi de l'informatique.")

84
Bjarne

Oui définitivement. Le truc, c'est qu'aucune abstraction n'est parfaite. Tous les détails de la couche sur laquelle se trouvent les abstractions sont là pour une raison, et cela peut simplifier beaucoup de choses, mais si cette complexité n'était pas nécessaire à un moment donné, elle ne serait probablement pas là en premier lieu. Et cela signifie qu'à un moment donné, chaque abstraction va fuir d'une manière ou d'une autre.

Et c'est là que réside le vrai problème. Lorsque les abstractions échouent, plus vous en avez superposé entre le code que vous avez écrit et ce qui se passe réellement, plus il est difficile de comprendre le problème et de le résoudre, car il y a plus d'endroits où le problème pourrait être. Et plus il y a de couches, plus vous devez en savoir pour le retrouver.

32
Mason Wheeler

Oui absolument.

L'analogie que j'aime utiliser pour expliquer la programmation est celle d'un tailleur. Lors de la confection d'un costume, un bon tailleur laissera toujours une petite quantité de tissu à des endroits stratégiques dans le vêtement pour permettre au vêtement d'être rentré ou sorti sans changer sa forme ou sa structure générale.

Good Tailor ne laisse pas de rames de tissu à chaque couture, au cas où vous auriez un troisième bras ou une grossesse. Trop de matériel aux mauvais endroits rendra un vêtement mal ajusté et mal porté, le tissu supplémentaire gêne simplement l'utilisation normale. Pour peu de tissu et le vêtement est sujette aux larmes et ne pourra pas être modifié pour faire face à des changements mineurs dans le physique de son porteur, affectant ainsi la position du vêtement.

Peut-être qu'un jour, notre bon tailleur sera chargé de confectionner une robe si serrée qu'il devra en coudre le port. Et peut-être que notre bon tailleur est invité à faire des vêtements de maternité, où le style et la coupe sont en second lieu au confort et à la capacité d'extension. Mais avant d'entreprendre l'un ou l'autre de ces emplois spéciaux, un bon tailleur serait assez sage pour sensibiliser tout le monde aux compromis qui sont faits pour atteindre ces objectifs.

Parfois, ces compromis sont la bonne voie à suivre et les gens sont prêts à accepter leurs conséquences. Mais dans la plupart des cas, l'approche de laisser un peu là où cela compte le plus l'emportera sur les avantages perçus.

Donc, relier cela à l'abstraction. Il est tout à fait possible qu'il y ait trop de couches d'abstraction, tout comme il est possible d'en avoir trop peu. Le véritable art du programmeur, comme nos amis tailleurs, est de laisser un peu où cela compte le plus.

Revenir sur le sujet.

Le problème avec le code n'est généralement pas l'abstraction, mais les dépendances. Comme vous l'avez souligné, c'est le code qui relie les objets discrets qui pose problème, car il y a une dépendance implicite entre eux. À un moment donné, la communication entre choses doit simplement être concrète, mais juger de l'endroit où se trouve ce point nécessite généralement une certaine conjecture.

Cela étant dit, tout ce qui est "Micro" est généralement une indication que vous avez surexploité la disposition de votre objet et que vous utilisez probablement Type comme synonyme de ce qui devrait être Data. Avoir moins de choses signifie également moins de dépendances nécessaires pour communiquer entre elles.

Je suis un grand fan de la messagerie asynchrone entre les systèmes pour cette raison. Vous vous retrouvez avec deux systèmes dépendant du message, plutôt que l'un de l'autre. Vous offrant un couplage moins étroit entre les systèmes communicants. À ce stade, si vous devez avoir une dépendance entre les systèmes, vous devez déterminer si vous avez les bits qui dépendent au bon endroit ou aux bons endroits. Et c'est souvent le cas que vous n'avez pas.

Enfin, le code compliqué va être compliqué. Il n'y a souvent aucun moyen de contourner cela. Mais un code qui a moins de dépendances est beaucoup plus facile à comprendre qu'un code qui repose sur divers états externes.

18
Matt D

Je pense que notre objectif est de fournir de bonnes abstractions sur le modèle de domaine et la logique métier donnés.

J'ai un point de vue différent: notre objectif est de résoudre un problème commercial. Les abstractions ne sont qu'une technique pour organiser une solution. Une autre réponse utilise l'analogie d'un tailleur faisant des vêtements. J'ai une autre analogie à laquelle j'aime penser: un squelette. Le code est comme un squelette et les abstractions sont les articulations entre les os. Si vous n'avez pas d'articulations, vous vous retrouvez avec un seul os qui ne peut pas bouger du tout et qui est inutile. Mais si vous avez trop d'articulations, vous vous retrouvez avec un tas de gelée bâclée qui ne peut pas tenir debout toute seule. L'astuce consiste à trouver le bon équilibre - suffisamment de joints pour permettre le mouvement, mais pas tellement qu'il n'y a pas de forme ou de structure définie.

13
Jordan Lev

Une chose qui n'a pas été mentionnée jusqu'à présent est le principe KISS et YAGNI : Restez simple et stupide; Tu n'en auras pas besoin.

Parfois, il y a de très bonnes raisons pour les abstractions:

  • Vous les utilisez déjà et vous en avez déjà besoin pour maintenir la complexité faible - peut-être pas à l'avenir, mais en fait déjà maintenant.
  • Vous écrivez une bibliothèque/quelque chose avec un système de plug-in et vous voulez empêcher les changements de rupture pour vos utilisateurs.

Si ce n'est pas le cas, vous pouvez peut-être supprimer les abstractions.

1
Martin Thoma