Quelle est la valeur de masquer les détails par le biais d'abstractions? N'y a-t-il pas de valeur dans la transparence?


30

Contexte

Je ne suis pas un grand fan d'abstraction. J'admettrai que l'on peut bénéficier de l'adaptabilité, de la portabilité et de la réutilisation des interfaces, etc.

Il y a l'autre "avantage" majeur de l'abstraction, qui est de cacher la logique d'implémentation et les détails aux utilisateurs de cette abstraction. L'argument est que vous n'avez pas besoin de connaître les détails et que l'on devrait se concentrer sur leur propre logique à ce stade. Est logique en théorie.

Cependant, chaque fois que je gère des applications de grande entreprise, j'ai toujours besoin de plus de détails. Cela devient un énorme tracas de creuser de plus en plus profondément dans l'abstraction à chaque tour juste pour savoir exactement ce que fait quelque chose; c'est-à-dire avoir à faire une "déclaration ouverte" environ 12 fois avant de trouver la procédure stockée utilisée.

Cette mentalité de «cacher les détails» semble juste se mettre en travers. Je souhaite toujours des interfaces plus transparentes et moins d'abstraction. Je peux lire du code source de haut niveau et savoir ce qu'il fait, mais je ne saurai jamais comment il le fait, quand il le fait, c'est ce que j'ai vraiment besoin de savoir.

Que se passe t-il ici? Est-ce que tous les systèmes sur lesquels j'ai déjà travaillé sont mal conçus (du moins dans cette perspective)?

Ma philosophie

Lorsque je développe un logiciel, j'ai l'impression d'essayer de suivre une philosophie qui me semble étroitement liée à la philosophie ArchLinux :

Arch Linux conserve les complexités inhérentes à un système GNU / Linux, tout en les maintenant bien organisées et transparentes. Les développeurs et les utilisateurs d'Arch Linux pensent qu'essayer de cacher les complexités d'un système se traduit en fait par un système encore plus complexe et doit donc être évité.

Et donc, je n'essaye jamais de cacher la complexité de mon logiciel derrière des couches d'abstraction. J'essaie d'abuser de l'abstraction, pas d'en devenir l'esclave.

Question au coeur

  1. Y a-t-il une réelle valeur à cacher les détails?
  2. Ne sacrifions-nous pas la transparence?
  3. Cette transparence n'est-elle pas précieuse?

7
L'abstraction peut être abusée sous la forme d'une mauvaise conception. Mais cela ne signifie pas que l'abstraction n'est en principe pas valable.
Bernard

4
Je pense qu'il y a une bonne question là-dedans, cependant, cela ressemble beaucoup à une diatribe contre l'abstraction. Pourriez-vous minimiser cela et faire ressortir davantage votre véritable question.
PersonalNexus

4
Êtes-vous sûr d'utiliser la bonne définition de «cacher les détails»? Dans ce contexte, il s'agit de réduire le couplage , pas de vous empêcher d'apprendre le fonctionnement interne de quelque chose.
Andres F.

24
À moins que vous ne vouliez programmer avec un voltmètre et un oscilliscope à votre bureau, vous ne programmez que contre des abstractions au-dessus des abstractions au-dessus des abstractions. Y a-t-il une valeur pour vous à cacher le détail que vous manipulez en fait non pas des bits mais en fait des tensions? Est-ce que cela sacrifie la transparence? Cette transparence est-elle précieuse?
Eric Lippert

8
Je pense que ce avec quoi vous avez des problèmes n'est pas l'abstraction, ce sont des couches d'indirection vides qui n'abstiennent vraiment rien. Oui, ceux-ci se trouvent souvent dans les grands systèmes d'entreprise et non, ils ne sont pas bons.
Michael Borgwardt

Réponses:


47

La raison pour cacher les détails n'est pas de garder les détails cachés; c'est pour permettre de modifier l'implémentation sans casser le code dépendant.

Imaginez que vous avez une liste d'objets et que chaque objet possède une Namepropriété et d'autres données. Et souvent, vous devez trouver un élément dans la liste qui Namecorrespond à une certaine chaîne.

La manière la plus évidente consiste à parcourir chaque élément un par un et à vérifier s'il Namecorrespond à la chaîne. Mais si vous trouvez que cela prend beaucoup trop de temps (comme si vous aviez plusieurs milliers d'éléments dans la liste), vous pourriez le remplacer par une recherche par dictionnaire d'objets chaîne.

Maintenant, si toutes vos recherches ont été effectuées en récupérant la liste et en la parcourant, vous avez un énorme travail à faire pour résoudre ce problème. C'est encore plus difficile si vous êtes dans une bibliothèque et que des utilisateurs tiers l'utilisent; vous ne pouvez pas sortir et réparer leur code!

Mais si vous aviez une FindByNameméthode pour encapsuler le processus de recherche de nom, vous pouvez simplement changer la façon dont il est implémenté et tout le code qui l'appelle continuera de fonctionner, et sera beaucoup plus rapide et gratuit. C'est la vraie valeur de l'abstraction et de l'encapsulation.


Je suis d'accord avec cela, mais ce n'était pas le but de la question. Je peux entièrement convenir qu'une méthode pour encapsuler des fonctionnalités spécifiques est utile, mais je pense que ce n'est pas toujours le motif. Ai-je tort?
user606723

3
@ User606723: Ça devrait l'être. Cela ne signifie pas toujours est . Certaines personnes ne comprennent pas le point et empilent simplement plus de couches sur plusieurs couches et transforment les choses en désordre. C'est pourquoi les programmeurs expérimentés conseillent aux nouveaux développeurs de ne jamais adopter une nouvelle technologie ou technique avant d'avoir compris.
Mason Wheeler

3
@ user606723: La transparence encourage un couplage serré, donc ce n'est peut-être pas toujours mauvais, mais c'est généralement le cas.
Malfist

3
Le problème que Mason décrit, des gens qui empilent des couches dans une forme de programmation culte, est pourquoi je me méfie de trop d'héritage et pourquoi la composition devrait être privilégiée par rapport à l'héritage. Semble être particulièrement un problème pour les programmeurs Java.
jhocking

2
Non, un couplage serré n'est pas toujours mauvais. C'est souvent mauvais, mais aller trop loin pour l'éviter peut entraîner des problèmes encore plus graves, tels que le codage logiciel.
Mason Wheeler

16

Je viens de terminer la lecture de la section Code complet sur l'abstraction, c'est donc là que la plupart de ces sources.

Le point d'abstraction est de supprimer la nécessité de demander "comment est-ce mis en œuvre?". Lorsque vous appelez user.get_id(), vous savez que idc'est ce que vous allez récupérer. Si vous devez demander "comment cela obtient-il l'ID utilisateur?" alors vous n'avez probablement pas besoin d'un idou get_id()retourne quelque chose d'inattendu et de mal conçu.

Vous utilisez l'abstraction pour vous permettre de concevoir:

a house with doors and windows

pas de conception

a box with four walls,
    with 3 holes,
        two of which fit panes of glass surrounded by wood frames,
        one that fits a large plank of wood with hinges and a metal knob,
etc.

Je ne parle pas de ces interfaces. Ces interfaces sont très bien. Je parle d'énormes systèmes complexes qui sont segmentés derrière de nombreuses abstractions différentes.
user606723

9
@ user606723 Ensuite, votre question concerne la conception trop complexe, plutôt que les abstractions
Andres F.

3
+1 pour le code complet. Il explique à la fois pourquoi l'abstraction est nécessaire à la conception et pourquoi le mauvais niveau d'abstraction entrave la conception. Pour aller plus loin dans votre exemple, si je travaille au bureau de zonage, je veux penser à vos maisons, sans rentrer dans les détails. Mais si vous pensiez aux maisons comme je le fais, vous ne pourriez pas en construire une.
Spencer Rathbun

cette question devrait être fermée ou le titre changé
Jake Berger

8

Y a-t-il une réelle valeur à cacher les détails?

Oui. En présentant des abstractions, nous pouvons penser et programmer à un niveau supérieur.

Imaginez modéliser des systèmes physiques sans calcul ni algèbre matricielle. C'est complètement impraticable. De même, si nous ne pouvons programmer qu'à un niveau scalaire, nous ne pourrons pas résoudre des problèmes intéressants. Même des applications Web relativement simples peuvent grandement bénéficier d'abstractions telles que les bibliothèques de balises. Il est beaucoup plus facile d'insérer une balise qui signifie «champs de saisie d'adresse» que de créer à plusieurs reprises quatre champs de texte et une zone de sélection. Et si vous décidez de vous développer à l'étranger, vous pouvez simplement modifier la définition de la balise, plutôt que de corriger chaque formulaire pour gérer les adresses internationales. L'utilisation efficace de l'abstraction est ce qui rend certains programmeurs dix fois plus efficaces que d'autres.

Les humains ont une mémoire de travail limitée. L'abstraction nous permet de raisonner sur les grands systèmes.

Aren't we sacrificing transparency?

Non. Si les abstractions ne sont pas utilisées, le but d'un composant logiciel est enfoui dans des détails répétés. Les développeurs passent leurs journées à parcourir le code comme ceci:

for (i = 0; i < pilgrim.wives.size(); ++i) {
  wife = pilgrim.wives[i];
  for (j = 0; j < wife.sacks.size(); ++j) {
     sack = wife.sacks[i];
     for (k = 0; j < sack.cats.size(); ++j) {
        cat = sack.cats[k];
        for (m = 0; m < cat.kits.size(); ++m) {
           ++count;
        }
     }
  }
}

et penser "oh oui une autre boucle à quatre niveaux sur les kits", au lieu de voir

pilgrim.kits.each { ++count; }

Cette transparence n'est-elle pas précieuse?

Comme vous l'avez souligné, l'indirection a un coût. Il est inutile de créer des calques "au cas où". Utilisez l'abstraction pour réduire la duplication et clarifier le code.


7

Quand les gens disent que les abstractions cachent les détails de l'implémentation, cela ne signifie pas réellement "cacher" dans le sens où il est difficile à trouver. Ce qu'ils signifient, c'est séparer les détails d'implémentation de l'interface publique, pour garder l'interface simple, concise et gérable. Tout comme une voiture "cache" la plupart de ses parties vitales, et n'offre qu'un ensemble de commandes assez rudimentaire pour les faire fonctionner, un module logiciel "cache" la plupart de ses fonctionnalités au fond de ses entrailles et n'expose qu'un nombre limité de méthodes d'accès à conduit le. Imaginez une voiture où vous deviez faire fonctionner manuellement tous les composants internes du moteur (et il y en a beaucoup), vous auriez vraiment du mal à garder un œil sur la circulation et à trouver le chemin.

Mais garder l'interface simple n'est pas simplement une chose esthétique; cela peut faire la différence entre un projet réussi et une marche de la mort. Jouons l'avocat du diable pendant une minute; imaginez un projet logiciel sans aucune abstraction. Si vous devez conserver une valeur, vous utilisez une variable globale. Si vous devez utiliser plusieurs fois la fonctionnalité, vous la copiez-collez. Si vous avez besoin de deux versions différentes d'une certaine section de code, vous copiez-collez, encapsulez-la dans une ifinstruction et modifiez les deux branches. Techniquement parlant, cela fonctionne, mais quelques mois plus tard, vous allez vous battre contre quelques problèmes vraiment désagréables:

  • Lorsque vous trouvez et corrigez un bogue, il est probable qu'il existe également dans d'autres instances copiées-collées de code similaire.Par conséquent, en plus de rechercher et de corriger le bogue, vous devez également rechercher d'autres occurrences et les corriger également.
  • Pour trouver un bogue ou implémenter un changement, un programmeur de maintenance doit être capable de comprendre le code correspondant. La difficulté à le faire augmente avec la taille de la section de code pertinente, mais encore plus avec sa portée. Garder une demi-douzaine de variables dans votre tête tout en parcourant mentalement du code est faisable; mais si vous en avez quelques centaines, votre productivité est gravement affectée (j'aime comparer le processus de réflexion avec un programme qui manque de RAM physique et doit plonger dans le fichier d'échange: au lieu de lire le code couramment en une seule fois , le programmeur doit faire des allers-retours pour rechercher les choses).
  • La portée d'un morceau de code a également un impact sur la taille de la base de code que l'on doit parcourir pour trouver le bogue. Si vous avez une fonction à dix lignes avec deux paramètres, et aucun global, et que vous connaissez les valeurs de l'entrée et la ligne à laquelle il se bloque, trouver le bogue est généralement trivial et ne nécessite souvent rien de plus que de regarder le code. Si c'est quelques centaines de lignes, vingt paramètres, quinze globales et appelle quelques autres fonctions de nature similaire, vous êtes dans une situation très pénible.
  • Sans abstraction appropriée, tout changement peut potentiellement affecter de grandes parties de la base de code, car pratiquement tout peut dépendre du code à modifier. Un symptôme typique avec de telles bases de code est que vous effectuez un petit changement apparemment innocent et qu'une fonctionnalité complètement indépendante se casse soudainement. Avec l'abstraction, vous pouvez limiter la quantité de dégâts qu'une modification peut causer et rendre l'impact plus prévisible. Si vous modifiez le nom d'un champ privé, vous n'avez qu'un seul fichier source à vérifier; si vous changez le nom d'une variable globale, vous devez parcourir toute la base de code.

Dans une base de code mal abstraite, l'impact augmente généralement de façon exponentielle avec la taille de la base de code, c'est-à-dire que l'ajout d'une quantité constante de code augmente l'effort de maintenance d'un facteur constant. Pour aggraver les choses, l'ajout de programmeurs à un projet n'augmente pas la productivité de manière linéaire, mais au mieux logarithmique (car plus votre équipe est grande, plus la charge nécessaire pour la communication est importante).


2

Je pense que vous devriez comprendre comment cela fonctionne si nécessaire. Une fois que vous avez déterminé qu'il fait ce que vous pensiez qu'il ferait, alors vous avez la tranquillité d'esprit. Je n'ai jamais pensé que le but était de le cacher à jamais.

Une fois que vous avez réglé une alarme sur une horloge dont vous êtes certain qu'elle fonctionnera, vous pouvez dormir un peu en sachant qu'elle se déclenchera au moment correct. Se réveiller une heure plus tôt pour pouvoir regarder les secondes défiler est un gaspillage.


1
Bien sûr, mais on ne me demande pas souvent de changer le fonctionnement de mon réveil, ni de demander à d'autres personnes de changer le fonctionnement de mon réveil sans être pleinement informés des changements.
user606723

1
Voyez-vous les changements de code pour chaque framework que vous avez déjà utilisé?
JeffO

1
non, mais je ne maintiens pas les modifications de code pour chaque framework que j'ai jamais utilisé.
user606723

3
Vous avez à peu près prouvé le point de vue de JeffO: vous n'êtes pas non plus dans l'entretien de réveils. Si vous en achetez un et commencez à l'utiliser sans faire un démontage complet et une analyse de son fonctionnement, vous avez accepté que l'intérieur soit abstrait pour vous. Rien ne dit que vous ne pouvez pas le déchirer pour savoir comment cela fonctionne, mais à quelle fréquence le trouvez-vous nécessaire?
Blrfl

2

Pour répondre spécifiquement à vos questions:

Y a-t-il une réelle valeur à cacher les détails?

Oui. Comme vous le reconnaissez dans la première ligne de votre question.

Ne sacrifions-nous pas la transparence?

Pas vraiment. Une abstraction bien écrite facilitera la compréhension des détails si nécessaire.

Cette transparence n'est-elle pas précieuse?

Oui. Les abstractions doivent être conçues et mises en œuvre pour faciliter la compréhension des détails lorsque cela est nécessaire / souhaité.


Enfin une réponse que j'aime.
user606723

1

Je dirais que cacher les détails est génial lorsque les choses cachées fonctionnent.

Par exemple, supposons que nous développions une interface qui définit les comportements (c.-à-d. GetListItems, SendListItem), qui sont deux fonctionnalités qui sont lancées par l'utilisateur via un clic sur un bouton ou quelque chose comme ça. MAINTENANT, chaque utilisateur peut avoir son propre "ListItemStore". l'un sur Facebook, ceux sur myspace .. (par exemple) .. et dire qu'il est enregistré en tant que propriété / paramètre utilisateur quelque part dans l'application via les préférences utilisateur .. et dire qu'il est possible pour les développeurs d'applications d'ajouter des ListItemStore supplémentaires au cours de temps (mybook, facespace, etc.)

maintenant, il y a beaucoup de détails pour se connecter à Facebook et obtenir des articles .. et il y a autant de détails lors de la connexion à myspace .. et ainsi de suite ...

maintenant, après l'écriture du code "d'accès au magasin" initial, il n'est peut-être pas nécessaire de le modifier (enfin dans le cas de facebook, nous avons probablement besoin d'un développeur à temps plein pour suivre les changements, zing ..),

donc quand vous utilisez le code, c'est quelque chose comme:

    new ItemManager(user) //passes in user, allowing class to get all user properties
    ItemManager.GetListItems()

et maintenant vous avez les données de l'Utilisateur de l'endroit où elles les ont stockées, et puisque tout ce qui m'inquiète, c'est d'obtenir la liste des éléments et de faire quelque chose avec, et comme cela n'a pris que 2 lignes qui fonctionneront, peu importe comment beaucoup plus de magasins sont ajoutés je peux revenir à répondre / poster des questions sur la pile ... lol ..

Donc, toute la plomberie pour y arriver est "cachée" et qui se soucie vraiment de la façon dont elle le fait tant que j'obtiens la bonne liste d'articles. Si vous avez des tests unitaires, vous pouvez même vous reposer plus facilement car les résultats devraient ' ont déjà été quantifiés ..


Oui, mais la partie qui doit être maintenue n'est jamais ces deux lignes. C'est impossible à foutre. C'est toujours le 2e, 3e, 4e niveau vers le bas que vous devez changer. Je serais d'accord pour dire que c'est génial quand vous avez une API en laquelle vous pouvez avoir confiance pour être stable , mais que se passe-t-il si elle ne peut pas être stable simplement par la complexité de votre entreprise?
user606723

1
@ user606723 si votre API n'est pas stable, alors elle est probablement immature, ou plus probablement la mauvaise abstraction
sdg

@ user606723 - C'est vrai, et dans ces cas, les conventions de dénomination elles-mêmes devraient définir une certaine forme de transparence qui cache la logique / le détail de la programmation réelle. Donc, en substance, la transparence peut être accomplie avec une dénomination correcte tout le long, cependant, nous ne devrions pas vraiment avoir besoin de descendre très souvent.
hanzolo

@sdg, ou parce que les exigences du système sont en constante évolution. Parce que les lois changent, parce que les API des autres changent, c'est hors de notre contrôle.
user606723

1
@ user606723 - Je travaille en fait sur un système financier SaaS et je vois les pièges de ne pas avoir suffisamment d'abstraction à chaque cycle de publication. une partie est le résultat d'une conception médiocre, mais c'est généralement le résultat de pousser le code là où il n'était pas initialement prévu. Descendre la chaîne peut être douloureux sans ces commentaires, noms et encapsulation . si tout ce que j'avais à faire était Remeasurements.GetRemeasureType (grant), puis reMeasure.PerformCalc (), ce serait tellement plus agréable que d'ajouter des surcharges et de lire les différentes branches logiques pour arriver au calcul approprié ou en ajouter une nouvelle
hanzolo

1

Ce que vous appelez «cacher», beaucoup le voient comme une séparation des préoccupations (par exemple, mise en œuvre vs interface).

À mon avis, un avantage majeur de l'abstraction est de réduire l'encombrement des détails inutiles de l'espace cérébral limité du développeur.

Si le code d'implémentation était obscurci, je pourrais voir cela comme un obstacle à la transparence, mais l'abstraction telle que je la vois, est juste une bonne organisation.


1

Tout d'abord, tout ce qui dépasse une seule instruction de code machine est essentiellement une abstraction - une while...doboucle est un moyen symbolique cohérent de représenter les comparaisons et les appels d'adresse requis pour répéter un ensemble d'instructions jusqu'à ce qu'une condition soit remplie. De même, le type int est une abstraction pour X nombre de bits (selon votre système). La programmation est une question d'abstraction.

Vous conviendrez probablement que ces abstractions primitives sont très utiles. Eh bien, c'est aussi la possibilité de construire le vôtre. OOAD et OOP sont tout au sujet.

Supposons que vous ayez une exigence où les utilisateurs veulent pouvoir exporter les données d'un écran dans une variété de formats: texte délimité, Excel et PDF. N'est-il pas pratique que vous puissiez créer une interface appelée "Exporter" avec une méthode d'exportation (données), sur la base de laquelle vous pouvez créer un DelimitedTextExporter, un ExcelExporter et un PDFExporter, chacun sachant comment créer sa sortie particulière? Tout ce que le programme appelant doit savoir, c'est qu'il peut appeler la méthode d'exportation (données), et quelle que soit l'implémentation utilisée, elle fera l'affaire. De plus, si les règles de texte délimité changent, vous pouvez modifier le DelimitedTextExporter sans avoir à jouer avec ExcelExporter, voire le casser.

Presque tous les modèles de conception bien connus utilisés dans la programmation OO dépendent de l'abstraction. Je recommanderais de lire Freeman et les modèles de conception Head First de Freeman pour mieux comprendre pourquoi l'abstraction est une bonne chose


1
même le code machine est une abstraction, il y a des processus réels, physiques, qui se déroulent sous la logique, même l'électronique numérique est une abstraction sur ce qui se passe réellement, comme quiconque a fait un hachage de l'overclocking d'une puce peut en témoigner
jk.

Trop vrai, même si cela semble plus concret au niveau de l'instruction machine.
Matthew Flynn

1

Je pense que je comprends votre sentiment à ce sujet, et je pense avoir une opinion similaire.

J'ai travaillé avec des développeurs Java qui transforment 50 classes de ligne de code en 3 classes et 3 interfaces car c'est facile à comprendre. Et je ne pouvais pas le supporter.

La chose était terriblement difficile à comprendre, presque impossible à déboguer et n'avait jamais eu besoin de "changer d'implémentation".

D'un autre côté, j'ai également vu du code où plusieurs objets partagent un comportement similaire et sont utilisés au même endroit, et pourraient vraiment utiliser des boucles de tri / traitement communes si les méthodes auraient été exposées via une interface commune.

Ainsi, à mon humble avis, les objets principaux qui sont susceptibles d'être utilisés dans des scénarios similaires bénéficient généralement d'un comportement commun qui devrait être accessible via l'interface. Mais c'est à peu près tout, abstraire des choses simples parce que c'est vrai, ou permet de changer d'implémentation est juste un moyen de rendre le code désordonné.

Là encore, je préfère des classes plus intelligentes plus longues à une quantité explosive de petites classes avec tous les problèmes de gestion de la vie, et des relations difficiles à voir et des graphiques d'appels de spaghetti. Donc, certaines personnes seront en désaccord avec moi.


1

L'objectif caché de la dissimulation et de l'abstraction devrait être de découpler l'utilisateur de la mise en œuvre afin qu'ils puissent être modifiés indépendamment.Si le consommateur est associé aux détails de la mise en œuvre, en raison de la manipulation de leurs composants internes, les deux sont coulés dans la pierre et il devient plus difficile d'introduire de nouvelles fonctionnalités ou de meilleurs algorithmes à l'avenir.

Lors de l'écriture d'un module, les parties cachées de l'implémentation vous donnent la tranquillité d'esprit de pouvoir les modifier sans risquer de casser un autre code auquel vous ne pouvez pas penser.

Un autre avantage de fournir des interfaces opaques est qu'elles réduisent considérablement la surface entre les sous-systèmes. En réduisant la quantité de façons dont ils peuvent interagir, ils peuvent devenir plus prévisibles, plus faciles à tester et avoir moins de bogues. Les interactions entre les modules augmentent également de façon quadratique avec le nombre de modules, il est donc utile d'essayer de contrôler cette croissance de complexité.


Cela dit, il est bien sûr possible d'en cacher trop et d'imbriquer les interfaces trop profondément. C'est le travail du programmeur, en tant qu'être humain intelligent, de concevoir le système afin qu'il soit extrêmement utile tout en minimisant la complexité et en maximisant la maintenabilité.


0

Dans de nombreux cas, vous n'avez tout simplement pas besoin de savoir comment les choses sont mises en œuvre. Je peux presque garantir que vous écrirez du code comme celui-ci people.Where(p => p.Surname == "Smith")tant de fois par jour, mais vous ne penserez presque jamais "comment cette Where()méthode fonctionne-t-elle réellement?" Vous ne vous en souciez pas - vous savez que cette méthode est là et qu'elle vous donne les résultats que vous souhaitez. Pourquoi voudriez-vous savoir comment cela fonctionne?

C'est exactement la même chose pour tout logiciel interne; ce n'est pas parce qu'il n'est pas écrit par Oracle, Microsoft, etc. que vous devriez avoir à chercher comment il est implémenté. Vous pouvez raisonnablement vous attendre à ce qu'une méthode appelée GetAllPeopleWithSurname(string name)vous renvoie une liste de personnes qui portent ce nom de famille. Il peut parcourir une liste, il peut utiliser un dictionnaire, il peut faire quelque chose de complètement fou, mais vous ne devriez pas vous en soucier .

Il y a bien sûr une exception à cette règle (ce ne serait pas une règle sans elle!) Et c'est s'il y a un bug dans la méthode. Donc, dans l'exemple ci-dessus, si vous avez une liste avec 3 personnes et que vous savez que l'un d'eux a le nom de famille Smith et qu'ils ne sont pas retournés dans la liste, alors vous vous souciez de la mise en œuvre de cette méthode car elle est clairement cassée .

L'abstraction, lorsqu'elle est effectuée correctement, est merveilleuse car elle vous permet de filtrer tout ce qui n'est pas utile lorsque vous devez le lire à une date ultérieure. N'oubliez pas que vous passez beaucoup plus de temps à lire du code qu'à l'écrire, l'accent doit donc être mis sur la simplification de cette tâche. Vous pensez peut-être aussi que l'abstraction signifie une hiérarchie d'objets aussi longtemps que votre bras, mais cela peut être aussi simple que de refactoriser une méthode de 100 lignes en 10 méthodes, chacune de 10 lignes de long. Donc, ce qui était autrefois 10 étapes regroupées est maintenant 10 étapes distinctes vous permettant d'aller directement à l'endroit où se cache ce bug embêtant.


Eh, mais disons que vous êtes celui qui maintient la mise en œuvre. Je veux dire, quelqu'un se soucie de la mise en œuvre, et cette personne est vous. Il se trouve que vous êtes également l'utilisateur de l'implémentation ... Les exigences métier changent (d'une manière que vous ne pouvez pas prédire) provoquant des changements qui se trouvent sur plusieurs couches. Je suppose que mon point est qu'un bug n'est pas la seule exception à cette règle.
user606723

Le problème estPeopleFactory.People.Strategy.MakePeople.(CoutryLaw.NameRegistry.NameMaker.Make()) as People.Female
Coder

@ user606723 Vous n'avez pas à vous soucier de chaque ligne de code dans votre base de code tout le temps. S'il y a un bogue dans cette implémentation ou si elle a été signalée comme devant être réécrite (parce qu'elle est lente, mal écrite ou autre) et que vous la réécrivez, alors vous vous en souciez. Sinon, il doit être gardé à l'écart. Peut-être que votre problème est que vous essayez de posséder tout le code tout le temps. Vous devriez simplement vous concentrer sur le code sur lequel vous travaillez à un moment particulier à mon avis.
Stuart Leyland-Cole

@Coder C'est évidemment une forme d'abstraction assez extrême! Au début, je pensais que c'était le genre de chose contre laquelle le PO était opposé, mais à la lecture du reste de leurs réponses, il semble qu'ils considèrent toute l' abstraction comme une mauvaise mauvaise mauvaise. C'est pourquoi j'ai essayé d'expliquer les différents niveaux d'abstraction dans ma réponse.
Stuart Leyland-Cole

0

Les abstractions entraînent la dissimulation d'informations. Cela devrait se retrouver dans l'accouplement inférieur. Cela devrait entraîner moins de risques de changement. Cela devrait conduire les programmeurs heureux à ne pas devenir nerveux en touchant le code.

Ces idées s'expriment à travers trois lois essentielles de l'architecture logicielle:

La loi de Simon: "Les hiérarchies réduisent la complexité." (Les hiérarchies introduisent l'abstraction)

La loi de Parna: "Seul ce qui est caché peut être changé sans risque."

Loi de Constantin: les programmes robustes nécessitent un faible couplage et une forte cohésion


"Les hiérarchies réduisent la complexité." - pas nécessairement vrai.
Coder

Aucune méthodologie de conception n'est déterministe. Cette loi ne vient pas de l'IT / CS, elle est formulée dans un sens beaucoup plus large et est également référée par les mathématiques, les physiciens, etc. C'est un principe valable, mais personne ne peut vous empêcher de créer des Hiérarchies absurdes.
ins0m

0

Je suis également dans le domaine des applications d'entreprise, et cette question a attiré mon attention parce que j'ai la même question moi-même. J'ai eu un aperçu de la question de l'abstraction au cours de ma carrière jusqu'à présent, mais mon aperçu n'est en aucun cas la réponse universelle. Je continue à apprendre / écouter de nouvelles idées et pensées, donc ce que je crois maintenant peut changer.

Lorsque je maintenais une application de soins de santé vaste et complexe, juste comme vous, je détestais toutes les abstractions là-bas. Déterminer où va tout le code était une douleur dans le cou. Sauter dans différentes classes m'a donné le vertige. Alors je me suis dit "l'abstraction craint, je minimiserai l'abstraction quand je crée des trucs".

Ensuite, il est venu le temps de concevoir une application (composant de service Web relativement petit) à partir de zéro. En me souvenant de toute la douleur, j'avais une conception assez plate du composant. Le problème était que lorsque les exigences changeaient, je devais changer à de nombreux endroits différents (les exigences étaient assez fluides et je ne pouvais rien y faire). C'était tellement mauvais, j'ai essentiellement abandonné ma conception initiale et ma re-conception avec l'abstraction, et les choses se sont améliorées - je n'ai plus eu à apporter de modifications à de nombreux endroits lorsque les exigences ont changé.

J'ai livré l'application, je me suis assis pendant quelques semaines, puis on m'a dit de commencer à maintenir l'application. Cela faisait un moment que je ne me souvenais pas de tout, j'ai donc lutté un peu pour comprendre mon propre code, et l'abstraction n'aidait pas.

J'ai ensuite été mis sur de nombreux autres projets différents et j'ai eu la chance de jouer avec les niveaux d'abstraction un peu plus. Ce que je trouve en fait, et ce n'est que mon opinion personnelle, l'abstraction aide beaucoup au développement mais a un impact négatif lorsque vous n'avez pas écrit le code et essayez de tout comprendre au niveau le plus profond d'une application; vous passerez plus de temps à sauter dans différentes classes et à essayer de faire le lien.

Mon sentiment est que l'abstraction est si précieuse pendant le temps de développement que le problème que nous traversons en tant que responsable en essayant de comprendre le code en vaut la peine. Les logiciels existent pour résoudre les problèmes commerciaux, les problèmes commerciaux évoluent avec le temps; par conséquent, le logiciel doit évoluer au fil du temps. Sans abstraction, l'évolution du logiciel est très difficile. On peut concevoir l'abstraction de manière à ce que le responsable puisse naviguer facilement dans la base de code une fois qu'il a vu le modèle de la structure du code, de sorte que seule la courbe d'apprentissage initiale est frustrante.


0

Comme d'autres l'ont dit, «cacher des détails» derrière une abstraction permet de les modifier sans affecter les utilisateurs. Cette idée vient de Parnas ' On the Criteria to be used in Decomposing Systems Into Modules (1972) , et est liée à l'idée de types de données abstraits (ADT) et de programmation orientée objet.

À peu près à la même époque, Un modèle relationnel de données de Codd pour les grandes banques de données partagées (1970) était motivé (voir le résumé et l'intro) en voulant changer la représentation de stockage interne des bases de données, sans affecter les utilisateurs de la base de données. Il avait vu des programmeurs prendre régulièrement des jours, modifiant des pages de code, pour faire face à des changements de stockage mineurs.

Cela dit, une abstraction n'est pas très utile si vous devez voir ce qu'elle contient pour pouvoir l'utiliser. Il peut être très difficile de bien le concevoir. Un exemple d'une bonne abstraction est l'addition - à quand remonte la dernière fois où vous avez dû penser à ce qui se passe à l'intérieur? (mais parfois vous le faites, par exemple pour un débordement).

Le problème essentiel (à mon humble avis) est que pour bien concevoir les modules (au sens de Parnas), vous devez prévoir ce qui changera et ce qui ne changera pas. Il est difficile de prédire l'avenir - mais si vous avez beaucoup d'expérience avec quelque chose et que vous le comprenez clairement, vous pouvez faire un très bon travail de prévision. Et par conséquent, vous pouvez concevoir un module (abstraction) qui fonctionne bien.

Cependant, il semble que le sort de toutes les abstractions - même les meilleures - que finalement il y aura des changements imprévus (et sans doute, imprévisibles) qui nécessitent de briser l'abstraction. Pour résoudre ce problème, certaines abstractions ont un échappement, où vous pouvez accéder à un niveau plus profond si vous en avez vraiment besoin.

Tout cela semble très négatif. Mais je pense que la vérité est que nous sommes entourés d'abstractions qui fonctionnent si bien que nous ne les remarquons pas ou ne réalisons pas ce qu'elles cachent. Nous ne remarquons que les pauvres abstractions, nous en avons donc une vue jaunie.


0

Les abstractions sont principalement à l'avantage de leurs consommateurs (par exemple, les programmeurs d'applications). Les programmeurs système (concepteurs) ont plus de travail à faire pour les rendre beaux et utiles, c'est pourquoi un bon design n'est généralement pas fait par les débutants.

Peut-être que vous n'aimez pas les abstractions car elles ajoutent toujours de la complexité? Peut-être que les systèmes sur lesquels vous avez travaillé avaient une abstractionite (surutilisation d'abstractions)? Ce n'est pas une panacée.

Le travail supplémentaire et la complexité d'une abstraction utile devraient porter leurs fruits, mais il est difficile de le savoir avec certitude. Si vous considérez une abstraction comme un point pivot, la conception du logiciel peut fléchir de chaque côté: les implémentations de l'abstraction peuvent être modifiées sans casser le code client, et / ou de nouveaux clients peuvent facilement réutiliser l'abstraction pour faire de nouvelles choses.

Vous pourriez presque mesurer le retour sur investissement des abstractions en montrant qu'elles ont été "fléchies" au fil du temps dans l'une ou les deux directions: changements de mise en œuvre relativement indolores et nouveaux clients supplémentaires.

Par exemple: en utilisant l'abstraction de la classe Socket en Java, je suis sûr que mon code d'application de Java 1.2 fonctionne toujours bien sous Java 7 (bien que leurs performances puissent être modifiées). Depuis Java 1.2, il y a certainement eu beaucoup de nouveaux clients qui ont également utilisé cette abstraction.

En ce qui concerne le mécontentement des abstractions, si je parlais aux développeurs qui ont maintenu le code derrière la classe Socket, leur vie n'est peut-être pas aussi peachy et rose que les clients qui ont utilisé Socket pour écrire des applications amusantes. Travailler sur la mise en œuvre d'une abstraction est certainement plus de travail que de l'utiliser. Mais cela ne fait pas de mal.

En ce qui concerne la transparence, dans une stratégie de conception descendante, une transparence totale entraîne une mauvaise conception. Les programmeurs intelligents ont tendance à tirer le meilleur parti des informations dont ils disposent, et le système devient alors étroitement couplé. Le plus petit changement de détail (par exemple, changer l'ordre des octets dans une structure de données) dans un module peut casser du code ailleurs, car un programmeur intelligent a utilisé ces informations pour faire quelque chose d'utile. David Parnas a souligné ce problème dans des articles datant de 1971 où il proposait de cacher des informations dans les dessins.

Votre référence à ArchLinux a du sens pour moi si vous considérez "l'intérieur" du système d'exploitation comme étant l'implémentation complexe de l'abstraction que sont l'OS des applications qui s'exécutent dessus. Restez simple dans les entrailles de l'abstraction.


0

Je répondrai à votre question par une question; lorsque vous avez conduit au travail ce matin (je suppose que vous l'avez fait en fait), vous êtes-vous soucié exactement de la façon dont le moteur a ouvert les soupapes pour laisser entrer les mélanges air-carburant, puis les a allumées? Non. Vous ne vous souciez pas du fonctionnement du moteur de votre voiture lorsque vous conduisez sur la route. Vous souciez qu'il fait le travail.

Supposons qu'un jour votre voiture ne fonctionne pas. Ne démarre pas, lance une tige, brise une ceinture, laboure inexplicablement cette barrière de béton sans aucune faute de votre part pendant que vous étiez occupé à envoyer des SMS. Maintenant, vous avez besoin d'une nouvelle voiture (au moins temporairement). Vous souciez-vous exactement du fonctionnement de cette nouvelle voiture? Non. Ce qui vous importe, c'est d'abord que cela fonctionne, et deuxièmement que vous pouvez utiliser les mêmes connaissances et compétences que vous avez utilisées pour conduire votre ancienne voiture pour conduire la nouvelle. Idéalement, il devrait vous sembler qu'il n'y a eu aucun changement dans la voiture que vous conduisez. De façon réaliste, le fonctionnement de cette nouvelle voiture devrait vous donner le moins de "surprises" possible.

Ces principes de base sont le principe de base derrière l'encapsulation et l'abstraction. La connaissance de la façon dont un objet fait ce qu'il fait ne devrait pas être une condition préalable à son utilisation pour faire ce qu'il fait. Même en programmation informatique, les détails des chemins électriques au sein du CPU exécutant votre programme sont résumés derrière au moins une demi-douzaine de couches d'instructions d'E / S, de pilotes, de logiciels OS et d'exécution. De nombreux ingénieurs logiciels très performants écrivent du bon code sans se soucier une seule fois de l'architecture matérielle exacte, ni même de la construction du système d'exploitation, qui l'exécutera. Y compris moi.

L'encapsulation / la dissimulation de l'information permet à la mentalité de "ne pas se soucier de la façon dont elle le fait, juste de s'en soucier". Votre objet doit exposer ce qui est utile au consommateur, d'une manière que le consommateur puisse facilement consommer. Maintenant, dans le monde réel, cela ne signifie pas qu'une voiture ne devrait pas donner à l'utilisateur des informations sur le fonctionnement interne, ou que la voiture ne devrait permettre à l'utilisateur que les fonctionnalités les plus élémentaires comme l'allumage, le volant, et pédales. Toutes les voitures ont des compteurs de vitesse et des jauges de carburant, des tachymètres, des lumières idiotes et d'autres commentaires. Pratiquement toutes les voitures ont également des commutateurs pour divers sous-systèmes indépendants, tels que les phares, les clignotants, la radio, le réglage des sièges, etc. Certaines voitures permettent une entrée utilisateur assez ésotérique, comme la sensibilité du différentiel central à glissement limité. Dans tous les cas, si vous en savez assez, vous pouvez l'ouvrir et changer les choses pour le faire fonctionner d'une manière légèrement différente. Mais, dans la plupart des cas, peut-être, juste peut-être, l'utilisateur ne devrait pas être en mesure de contrôler directement et indépendamment les pompes à carburant de l'intérieur de la cabine? Peut-être, juste peut-être, que l'utilisateur ne devrait pas être en mesure d'activer ses feux de freinage sans appuyer sur la pédale de frein?

L'abstraction permet la mentalité "ce n'est pas la même chose que cela, mais parce qu'ils sont tous les deux XI, je peux les utiliser comme je le ferais avec n'importe quelle mentalité X". Si votre objet hérite ou implémente une abstraction, vos consommateurs doivent s'attendre à ce que votre implémentation produise le même résultat ou un résultat similaire à d'autres implémentations connues de l'abstraction. Une Toyota Camry et une Ford Fusion sont toutes deux des «voitures». En tant que tels, ils ont un ensemble commun de fonctionnalités attendues, comme un volant. Tournez-le dans le sens antihoraire, la voiture va à gauche. Tournez-le dans le sens des aiguilles d'une montre, la voiture va à droite. Vous pouvez monter dans n'importe quelle voiture aux États-Unis et vous attendre à ce que la voiture ait un volant et au moins deux pédales, celle de droite étant la pédale "car go" et celle du centre étant la pédale "car stops" .

Un corollaire de l'abstraction est la "théorie du moindre étonnement". Si vous preniez le volant d'une nouvelle voiture pour un essai routier, tourniez le volant dans le sens des aiguilles d'une montre et que la voiture tournait à gauche, vous seriez assez étonné pour le moins. Vous accuseriez le revendeur de colporter un point de vente et vous auriez peu de chances d'écouter l'une de ses raisons pour lesquelles le nouveau comportement est "meilleur" que ce à quoi vous êtes habitué, ou si ce comportement est "documenté" ou comment " transparent "est le système de contrôle. Malgré cette nouvelle voiture et toutes les autres que vous avez conduites étant toujours des "voitures", lorsque vous conduisez cette voiture, vous devez changer certains concepts fondamentaux de la façon dont une voiture est censée être conduite afin de conduire la nouvelle voiture avec succès. C'est généralement une mauvaise chose, et cela ne se produit que lorsqu'il y a un avantage intuitif au nouveau paradigme. L'ajout de ceintures de sécurité est peut-être un bon exemple; Il y a 50 ans, vous venez de monter et de partir, mais maintenant vous devez boucler votre ceinture, l'avantage intuitif étant que vous ne passez pas par le pare-brise ou sur le siège passager en cas d'accident. Même alors, les conducteurs ont résisté; de nombreux propriétaires de voitures ont coupé les ceintures de sécurité de la voiture jusqu'à l'adoption de lois rendant leur utilisation obligatoire.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.