Signification de l'abstraction baveuse?

88

Que signifie le terme "Leaky Abstraction"? (Veuillez expliquer avec des exemples. J'ai souvent du mal à comprendre une simple théorie.)

Geonne
la source
duplication possible des interfaces Fluent et des abstractions qui fuient
missingfaktor
14
Vous voudrez peut-être lire l'article original de Joel Spolsky, The Law of Leaky Abstractions, qui, pour autant que je sache, est à l'origine du terme.
Daniel Pryden
1
La plupart des réponses de la dupe proposée concernent les interfaces fluides.
David Thornley
@David: Le deuxième post le plus voté répond à ce que signifie l'abstraction qui fuit, et avec un excellent exemple.
missingfaktor
4
Lorsque vous recherchez cette question sur Google 4 ans plus tard, il est difficile de deviner quel poste était le deuxième poste le plus voté.
John Reynolds

Réponses:

104

Voici un exemple d' espace de viande :

Les automobiles ont des abstractions pour les conducteurs. Dans sa forme la plus pure, il y a un volant, un accélérateur et un frein. Cette abstraction cache beaucoup de détails sur ce qu'il y a sous le capot: moteur, cames, courroie de distribution, bougies d'allumage, radiateur, etc.

La chose intéressante à propos de cette abstraction est que nous pouvons remplacer des parties de l'implémentation par des parties améliorées sans recycler l'utilisateur. Disons que nous remplaçons le capuchon du distributeur par un allumage électronique, et nous remplaçons la came fixe par une came variable. Ces changements améliorent les performances, mais l'utilisateur dirige toujours avec la roue et utilise les pédales pour démarrer et s'arrêter.

C'est en fait assez remarquable ... un jeune de 16 ou 80 ans peut faire fonctionner cette machine compliquée sans vraiment en savoir beaucoup sur son fonctionnement à l'intérieur!

Mais il y a des fuites. La transmission est une petite fuite. Dans une transmission automatique, vous pouvez sentir la voiture perdre de la puissance pendant un moment lorsqu'elle change de vitesse, tandis que dans la CVT, vous ressentez un couple régulier tout en montant.

Il y a aussi des fuites plus importantes. Si vous faites tourner le moteur trop vite, vous risquez de l'endommager. Si le bloc moteur est trop froid, la voiture peut ne pas démarrer ou avoir de mauvaises performances. Et si vous activez la radio, les phares et la climatisation en même temps, vous verrez votre consommation d'essence diminuer.

Mark E. Haase
la source
7
Merci pour l'exemple. Personne d'autre ne semblait être en mesure de fournir une explication simple.
Sebastian Patten
7
C'est une excellente réponse, d'autant plus qu'elle démontre le point de vue de l'utilisateur, qui est l'essence même de la version du logiciel.
chad
1
Que signifie meatspace? Explication profane?
brumScouse
1
@brumScouse "meatspace" signifie le monde physique hors ligne. Il est utilisé pour contraster avec le monde en ligne du cyberespace. Je modifierai ma réponse pour inclure un lien vers la définition.
Mark E. Haase
1
J'aime la façon dont cet article souligne qu '"il y a encore des fuites". Il s'agit de les minimiser.
alaboudi
50

Cela signifie simplement que votre abstraction expose certains des détails d'implémentation, ou que vous devez être conscient des détails d'implémentation lorsque vous utilisez l'abstraction. Le terme est attribué à Joel Spolsky , vers 2002. Voir l' article de wikipedia pour plus d'informations.

Un exemple classique est celui des bibliothèques réseau qui vous permettent de traiter les fichiers distants comme locaux. Le développeur qui utilise cette abstraction doit être conscient que des problèmes de réseau peuvent entraîner l'échec de cette opération contrairement aux fichiers locaux. Vous devez ensuite développer du code pour gérer spécifiquement les erreurs en dehors de l'abstraction fournie par la bibliothèque réseau.

Tvanfosson
la source
7
@mehaase Je ne vois pas en quoi il importe que votre abstraction soit fuyante par conception ou par négligence. J'ai élargi la réponse avec un exemple et plus d'informations de l'article référencé afin qu'il puisse fonctionner seul. De plus, je ne pense pas que "l'abstraction qui fuit" doive nécessairement être péjorative. Pour moi, cela décrit simplement une situation où vous, en tant que développeur, devez être plus prudent lorsque vous travaillez avec l'abstraction. La conception peut être bonne, mauvaise ou indifférente indépendamment de la «fuite».
tvanfosson le
12

Wikipédia a une assez bonne définition pour cela

Une abstraction qui fuit fait référence à toute abstraction implémentée, destinée à réduire (ou masquer) la complexité, où les détails sous-jacents ne sont pas complètement cachés

Ou en d'autres termes, pour les logiciels, c'est lorsque vous pouvez observer les détails de mise en œuvre d'une fonctionnalité via des limitations ou des effets secondaires dans le programme.

Un exemple rapide serait les fermetures C # / VB.Net et leur incapacité à capturer les paramètres ref / out. La raison pour laquelle ils ne peuvent pas être capturés est due à un détail de mise en œuvre de la façon dont le processus de levage se déroule. Cela ne veut pas dire cependant qu'il existe une meilleure façon de faire cela.

JaredPar
la source
12

Voici un exemple familier aux développeurs .NET: la Pageclasse d'ASP.NET tente de masquer les détails des opérations HTTP, en particulier la gestion des données de formulaire, afin que les développeurs n'aient pas à gérer les valeurs publiées (car elle mappe automatiquement les valeurs de formulaire au serveur les contrôles).

Mais si vous vous éloignez des scénarios d'utilisation les plus basiques, l' Pageabstraction commence à fuir et il devient difficile de travailler avec des pages à moins de comprendre les détails d'implémentation de la classe.

Un exemple courant est l'ajout dynamique de contrôles à une page - la valeur des contrôles ajoutés dynamiquement ne sera pas mappée pour vous sauf si vous les ajoutez au bon moment : avant que le moteur sous-jacent mappe les valeurs de formulaire entrantes aux contrôles appropriés. Lorsque vous devez apprendre cela, l'abstraction a fui .

Jeff Sternal
la source
Webforms avait le fond dans son seau. Ce qui est pire, c'est que les abstractions à peine voilées reviennent à travailler avec Http comme si vous travailliez dans une boîte à gants.
brumScouse
8

Eh bien, d'une certaine manière, c'est une chose purement théorique, mais pas sans importance.

Nous utilisons des abstractions pour rendre les choses plus faciles à comprendre. Je peux opérer sur une classe de chaîne dans une langue pour cacher le fait que je traite un ensemble ordonné de caractères qui sont des éléments individuels. Je traite un ensemble ordonné de caractères pour cacher le fait que je traite des nombres. Je traite des nombres pour cacher le fait que je traite des 1 et des 0.

Une abstraction qui fuit est celle qui ne cache pas les détails qu'elle est censée cacher. Si appelez string.Length sur une chaîne de 5 caractères en Java ou .NET, je pourrais obtenir n'importe quelle réponse de 5 à 10, en raison des détails d'implémentation où ce que ces langues appellent des caractères sont en réalité des points de données UTF-16 qui peuvent représenter 1 ou .5 d'un caractère. L'abstraction a fui. Cependant, ne pas la fuir signifie que trouver la longueur nécessiterait plus d'espace de stockage (pour stocker la longueur réelle) ou passer de O (1) à O (n) (pour déterminer la longueur réelle). Si je me soucie de la vraie réponse (souvent vous ne l'avez pas vraiment), vous devez travailler sur la connaissance de ce qui se passe réellement.

Des cas plus discutables se produisent avec des cas comme où une méthode ou une propriété vous permet d'entrer dans le fonctionnement interne, qu'il s'agisse de fuites d'abstraction ou de moyens bien définis de passer à un niveau d'abstraction inférieur, peuvent parfois être une question sur laquelle les gens ne sont pas d'accord.

Jon Hanna
la source
2
Et vous travaillez avec des 1 et des 0 pour cacher le fait que vous travaillez avec l'électronique et la physique (commentaire très tardif, je sais)
Davy8
6

Je vais continuer dans la veine de donner des exemples en utilisant RPC.

Dans le monde idéal de RPC, un appel de procédure à distance devrait ressembler à un appel de procédure locale (du moins c'est ce que dit l'histoire). Il doit être complètement transparent pour le programmeur de telle sorte que lorsqu'ils appellent, SomeObject.someFunction()ils n'ont aucune idée si SomeObject(ou juste someFunctiond'ailleurs) sont stockés localement et exécutés ou stockés et exécutés à distance. La théorie veut que cela simplifie la programmation.

La réalité est différente car il y a une ÉNORME différence entre faire un appel de fonction locale (même si vous utilisez le langage interprété le plus lent au monde) et:

  • appel via un objet proxy
  • sérialisation de vos paramètres
  • établir une connexion réseau (si elle n'est pas déjà établie)
  • transmission des données au proxy distant
  • demander au proxy distant de restaurer les données et d'appeler la fonction à distance en votre nom
  • sérialisation de la ou des valeurs de retour
  • transmission des valeurs de retour au proxy local
  • réassemblage des données sérialisées
  • renvoyer la réponse de la fonction à distance

Dans le temps seul, c'est environ trois ordres (ou plus!) De différence de grandeur. Ces trois ordres de grandeur vont faire une énorme différence dans les performances qui rendra votre abstraction d'un appel de procédure une fuite plutôt évidente la première fois que vous traitez à tort un RPC comme un véritable appel de fonction. De plus, un véritable appel de fonction, à moins de problèmes sérieux dans votre code, aura très peu de points de défaillance en dehors des bogues d'implémentation. Un appel RPC présente tous les problèmes possibles suivants qui seront considérés comme des cas d'échec au-delà de ce que vous attendez d'un appel local régulier:

  • vous ne pourrez peut-être pas instancier votre proxy local
  • vous ne pourrez peut-être pas instancier votre proxy distant
  • les proxys ne pourront peut-être pas se connecter
  • les paramètres que vous envoyez peuvent ne pas le rendre intact ou pas du tout
  • la valeur de retour que la télécommande envoie peut ne pas la rendre intacte ou pas du tout

Alors maintenant, votre appel RPC qui est "juste comme un appel de fonction locale" a tout un tas de conditions d'échec supplémentaires que vous n'avez pas à affronter lorsque vous faites des appels de fonction locale. L'abstraction a encore fui, encore plus fort.

En fin de compte, RPC est une mauvaise abstraction car il fuit comme un tamis à tous les niveaux - en cas de succès et en cas d'échec des deux.

JUSTE MON AVIS correct
la source
<pimp> J'aime mieux l'approche d'Erlang en ce sens qu'elle n'essaye pas de cacher la différence entre un appel de fonction et l'envoi d'un message à un processus au point que les deux utilisent une syntaxe très différente. Et un envoi de message de processus distant est très visiblement différent d'un envoi de processus local, bien qu'en utilisant la même syntaxe générale. </pimp>
JUSTE MON OPINION correcte
2
Eh bien, c'est la seule réponse qui donne réellement un bon exemple (compréhension de la lecture, les gens), donc il obtient mon +1.
Mark E. Haase
3

Un exemple dans l'exemple de django ORM many-to-many :

Notez dans l'exemple d'utilisation d'API que vous devez .save () l'objet Article de base a1 avant de pouvoir ajouter des objets Publication à l'attribut plusieurs-à-plusieurs. Et notez que la mise à jour de l'attribut plusieurs-à-plusieurs enregistre immédiatement dans la base de données sous-jacente, tandis que la mise à jour d'un attribut singulier n'est pas reflétée dans la base de données tant que .save () n'est pas appelé.

L'abstraction est que nous travaillons avec un graphe d'objets, où les attributs à valeur unique et les attributs à valeurs multiples ne sont que des attributs. Mais l'implémentation en tant que magasin de données basé sur une base de données relationnelle fuit ... alors que le système d'intégrité du RDBS apparaît à travers le mince placage d'une interface objet.

hash1baby
la source
1

Qu'est-ce que l'abstraction?

Tout d'abord, il vaut mieux comprendre ce qu'est «l'abstraction»?

L'abstraction est une manière de simplifier le monde. Cela signifie que vous n'avez pas à vous soucier de ce qui se passe réellement sous le capot / derrière le rideau. Cela signifie que quelque chose est une preuve idiote. Ok, alors qu'est-ce que ça veut dire? Ceci est mieux illustré par l'exemple.

Exemple d'abstraction: les complexités du pilotage d'un 737/747 sont "abstraites"

Prenons l'exemple d'un avion à réaction Boeing. Ces avions sont des machines très compliquées. Vous avez des moteurs à réaction, des systèmes d'oxygène, des systèmes électriques, des systèmes de train d'atterrissage, etc., mais le pilote n'a pas à se soucier des subtilités du moteur à réaction ... tout cela est "évacué", ce qui signifie: le jour, un pilote ne s'inquiète que de la roue et d'une colonne de commande pour contrôler l'avion. Gauche pour aller à gauche et droite pour aller à droite, tirez vers le haut pour gagner de l'élévation et poussez vers le bas pour descendre. C'est assez simple ... en fait j'ai menti: contrôler le volant est un peu plus compliqué. Dans un monde idéal, c'est la seule chose qu'il devraitêtre inquiet. Mais ce n'est pas le cas dans la vraie vie: si vous pilotez un avion comme un singe, sans aucune compréhension réelle du fonctionnement d'un avion, ni d'aucun des détails de mise en œuvre, vous vous écraserez probablement et tuerez tout le monde à bord.

Abstractions qui fuient

En réalité, un pilote doit s'inquiéter de BEAUCOUP de choses importantes - tout n'a pas été résumé: les pilotes doivent s'inquiéter de la vitesse du vent, de la poussée, des angles d'attaque, du carburant, de l'altitude, des problèmes météorologiques, des angles de descente, si le pilote va dans la bonne direction, où se trouve l'avion en ce moment et ainsi de suite. Les ordinateurs peuvent aider le pilote dans ces tâches, mais tout n'est pas automatisé / simplifié.

Par exemple, si le pilote tire trop fort sur la colonne - l'avion obéira, mais alors le pilote risquera de caler l'avion, et si vous décrochez l'avion, il est extrêmement difficile de reprendre le contrôle avant qu'il ne s'écrase au sol .

En d'autres termes, il ne suffit pas que le pilote contrôle simplement le volant sans rien savoir d'autre ......... nooooo ....... il doit connaître les risques et les limites sous-jacents de l'avion avant qu'il n'en vole un ....... elle doit savoir comment fonctionne l'avion, et comment l'avion vole; il doit connaître les détails de la mise en œuvre ..... elle doit savoir que tirer trop fort entraînera un décrochage, ou qu'un atterrissage trop raide détruira l'avion.

Ces choses ne sont pas abstraites. Beaucoup de choses sont abstraites, mais pas tout. Le pilote n'a qu'à se soucier de la colonne de direction, et peut-être d'une ou deux autres choses. L'abstraction est "fuyante".

...... c'est la même chose dans votre code. Si vous ne connaissez pas les détails de mise en œuvre sous-jacents, le plus souvent, vous travaillerez dans un coin.

Voici un exemple de codage:

Les ORM résument beaucoup de tracas dans le traitement des requêtes de base de données, mais si vous avez déjà fait quelque chose comme:

User.all.each do |user|
   puts user.name # let's print each user's name
end

Ensuite, vous vous rendrez compte que c'est une bonne façon de tuer votre application si vous avez plus de quelques millions d'utilisateurs. Tout n'est pas abstrait. Vous devez savoir que les appels User.allavec 25 millions d'utilisateurs vont augmenter votre utilisation de la mémoire et poser des problèmes. Vous devez connaître certains détails sous-jacents. L'abstraction est fuyante.

BKSpurgeon
la source
0

Le fait qu'à un moment donné , qui sera guidé par votre échelle et votre exécution, vous devrez vous familiariser avec les détails d'implémentation de votre cadre d'abstraction afin de comprendre pourquoi il se comporte de cette façon.

Par exemple, considérez cette SQLrequête:

SELECT id, first_name, last_name, age, subject FROM student_details;

Et son alternative:

SELECT * FROM student_details;

Maintenant, elles ressemblent à des solutions logiquement équivalentes, mais les performances de la première sont meilleures en raison de la spécification des noms de colonnes individuels.

C'est un exemple trivial, mais cela revient finalement à la citation de Joel Spolsky:

Toutes les abstractions non triviales, dans une certaine mesure, sont fuites.

À un moment donné, lorsque vous atteindrez une certaine échelle dans votre opération, vous voudrez optimiser le fonctionnement de votre base de données (SQL). Pour ce faire, vous devrez connaître le fonctionnement des bases de données relationnelles. Cela vous a été abstrait au début, mais il fuit. Vous devez l'apprendre à un moment donné.

Johnny
la source
-1

Supposons que nous ayons le code suivant dans une bibliothèque:

Object[] fetchDeviceColorAndModel(String serialNumberOfDevice)
{
    //fetch Device Color and Device Model from DB.
    //create new Object[] and set 0th field with color and 1st field with model value. 
}

Lorsque le consommateur appelle l'API, il obtient un Object []. Le consommateur doit comprendre que le premier champ du tableau d'objets a une valeur de couleur et le second champ est la valeur de modèle. Ici, l'abstraction a fui de la bibliothèque vers le code consommateur.

L'une des solutions consiste à renvoyer un objet qui encapsule le modèle et la couleur de l'appareil. Le consommateur peut appeler cet objet pour obtenir le modèle et la valeur de couleur.

DeviceColorAndModel fetchDeviceColorAndModel(String serialNumberOfTheDevice)
{
    //fetch Device Color and Device Model from DB.
    return new DeviceColorAndModel(color, model);
}
Niranjan Ramaiah
la source
-3

L'abstraction qui fuit consiste uniquement à encapsuler l'état. exemple très simple d'abstraction qui fuit:

$currentTime = new DateTime();

$bankAccount1->setLastRefresh($currentTime);
$bankAccount2->setLastRefresh($currentTime);
$currentTime->setTimestamp($aTimestamp);

class BankAccount {
    // ...

    public function setLastRefresh(DateTimeImmutable $lastRefresh)
    {
        $this->lastRefresh = $lastRefresh;
    } }

et la bonne manière (pas d'abstraction qui fuit):

class BankAccount
{
    // ...

    public function setLastRefresh(DateTime $lastRefresh)
    {
        $this->lastRefresh = clone $lastRefresh;
    }
}

plus de description ici .

Alireza Rahmani Khalili
la source