Che cosa è Leaky Abstraction?


90

Cosa significa il termine "Leaky Abstraction"? (Per favore, spiega con esempi. Spesso ho difficoltà a elaborare una semplice teoria.)



14
Potresti leggere l'articolo originale di Joel Spolsky The Law of Leaky Abstractions, che per quanto ne so è l'origine del termine.
Daniel Pryden

2
La maggior parte delle risposte dello stupido proposto riguardano interfacce fluenti.
David Thornley

@David: il secondo post più votato risponde a cosa significa astrazione leaky e con un ottimo esempio.
missingfaktor

4
Quando cerchi su Google questa domanda 4 anni dopo, è difficile indovinare quale post era il secondo più votato.
John Reynolds,

Risposte:


108

Ecco un esempio di meatspace :

Le automobili hanno astrazioni per i conducenti. Nella sua forma più pura, c'è volante, acceleratore e freno. Questa astrazione nasconde molti dettagli su cosa c'è sotto il cofano: motore, camme, cinghia di distribuzione, candele, radiatore, ecc.

La cosa bella di questa astrazione è che possiamo sostituire parti dell'implementazione con parti migliorate senza riqualificare l'utente. Supponiamo di sostituire il tappo dello spinterogeno con l'accensione elettronica e di sostituire la camma fissa con una camma variabile. Queste modifiche migliorano le prestazioni ma l'utente sterza ancora con il volante e utilizza i pedali per avviare e arrestare.

In realtà è davvero notevole ... un ragazzo di 16 o 80 anni può azionare questo complicato macchinario senza sapere davvero molto su come funziona all'interno!

Ma ci sono fughe di notizie. La trasmissione è una piccola perdita. In un cambio automatico puoi sentire l'auto perdere potenza per un attimo mentre cambia marcia, mentre in CVT senti una coppia fluida fino in fondo.

Ci sono anche perdite più grandi. Se giri il motore troppo velocemente, potresti danneggiarlo. Se il blocco motore è troppo freddo, l'auto potrebbe non avviarsi o potrebbe avere prestazioni scadenti. E se accendi la radio, i fari e l'aria condizionata allo stesso tempo, vedrai il tuo chilometraggio del gas diminuire.


8
Grazie per l'esempio. Nessun altro sembrava essere in grado di fornire una semplice spiegazione.
Sebastian Patten

8
Questa è un'ottima risposta, soprattutto perché dimostra il punto di vista dell'utente, che è l'essenza della versione del software.
Ciad

1
Cosa significa meatspace? Spiegazione laico?
brumScouse

1
@brumScouse "meatspace" significa il mondo fisico, offline. Viene utilizzato per contrastare il mondo online, cyberspazio. Modificherò la mia risposta per includere un collegamento alla definizione.
Mark E. Haase

1
Mi piace come questo post sottolinea che "ci sono ancora fughe di notizie". Si tratta di ridurli al minimo.
alaboudi

52

Significa semplicemente che la tua astrazione espone alcuni dettagli di implementazione o che devi essere consapevole dei dettagli di implementazione quando usi l'astrazione. Il termine è attribuito a Joel Spolsky , intorno al 2002. Vedi l' articolo di wikipedia per ulteriori informazioni.

Un classico esempio sono le librerie di rete che consentono di trattare i file remoti come locali. Lo sviluppatore che utilizza questa astrazione deve essere consapevole del fatto che i problemi di rete possono causare un errore in modi che non fanno i file locali. È quindi necessario sviluppare codice per gestire specificamente errori al di fuori dell'astrazione fornita dalla libreria di rete.


7
@mehaase Non vedo quanto sia importante se la tua astrazione perde per progettazione o per negligenza. Ho ampliato la risposta con un esempio e ulteriori informazioni dall'articolo di riferimento in modo che possa stare in piedi da solo. Inoltre, non penso che "l'astrazione che perde" debba necessariamente essere un peggiorativo. Per me descrive semplicemente una situazione in cui tu, come sviluppatore, devi stare più attento quando lavori con l'astrazione. Il design può essere buono, cattivo o indifferente indipendentemente dalla "perdita".
tvanfosson

13

Wikipedia ha una definizione abbastanza buona per questo

Un'astrazione che perde si riferisce a qualsiasi astrazione implementata, intesa a ridurre (o nascondere) la complessità, in cui i dettagli sottostanti non sono completamente nascosti

In altre parole, per il software è quando è possibile osservare i dettagli di implementazione di una funzionalità tramite limitazioni o effetti collaterali nel programma.

Un rapido esempio potrebbe essere le chiusure di C # / VB.Net e la loro incapacità di acquisire i parametri ref / out. Il motivo per cui non possono essere acquisiti è dovuto a un dettaglio di implementazione di come avviene il processo di sollevamento. Questo non vuol dire però che ci sia un modo migliore per farlo.


13

Ecco un esempio familiare agli sviluppatori .NET: ASP.NET Page classe tenta di nascondere i dettagli delle operazioni HTTP, in particolare la gestione dei dati del modulo, in modo che gli sviluppatori non debbano occuparsi dei valori inviati (perché mappa automaticamente i valori del modulo sul server controlli).

Ma se si va oltre gli scenari di utilizzo più elementari, l' Pageastrazione inizia a trapelare e diventa difficile lavorare con le pagine a meno che non si comprendano i dettagli di implementazione della classe.

Un esempio comune è l'aggiunta dinamica di controlli a una pagina: il valore dei controlli aggiunti dinamicamente non verrà mappato per te a meno che non vengano aggiunti al momento giusto : prima che il motore sottostante associ i valori del modulo in arrivo ai controlli appropriati. Quando devi imparare questo, l'astrazione è trapelata .


Webforms aveva un fondo nel suo secchio. Quel che è peggio è che le astrazioni sottilmente velate equivalgono a lavorare con Http come se stessi lavorando in un vano portaoggetti.
brumScouse

10

Ebbene, in un certo senso è una cosa puramente teorica, anche se non irrilevante.

Usiamo astrazioni per rendere le cose più facili da comprendere. Posso operare su una classe stringa in qualche lingua per nascondere il fatto che ho a che fare con un insieme ordinato di caratteri che sono elementi individuali. Ho a che fare con un insieme ordinato di caratteri per nascondere il fatto che ho a che fare con i numeri. Mi occupo di numeri per nascondere il fatto che ho a che fare con 1 e 0.

Un'astrazione che perde è quella che non nasconde i dettagli che intende nascondere. Se si chiama string.Length su una stringa di 5 caratteri in Java o .NET potrei ottenere qualsiasi risposta da 5 a 10, a causa dei dettagli di implementazione in cui ciò che quei linguaggi chiamano caratteri sono in realtà punti dati UTF-16 che possono rappresentare 1 o .5 di un personaggio. L'astrazione è trapelata. Non colare però significa che trovare la lunghezza richiederebbe più spazio di archiviazione (per memorizzare la lunghezza reale) o passare da O (1) a O (n) (per capire qual è la lunghezza reale). Se mi interessa la vera risposta (spesso non lo fai davvero) devi lavorare sulla conoscenza di ciò che sta realmente accadendo.

Casi più discutibili si verificano con casi come quelli in cui un metodo o una proprietà ti consente di entrare nel funzionamento interno, che si tratti di perdite di astrazione o di modi ben definiti per passare a un livello di astrazione inferiore, a volte può essere una questione su cui le persone non sono d'accordo.


2
E lavori con 1 e 0 per nascondere il fatto che stai lavorando con l'elettronica e la fisica (commento molto in ritardo, lo so)
Davy8

7

Continuerò a fornire esempi utilizzando RPC.

Nel mondo ideale di RPC, una chiamata di procedura remota dovrebbe apparire come una chiamata di procedura locale (o almeno così va la storia). Dovrebbe essere completamente trasparente per il programmatore in modo tale che quando chiamano SomeObject.someFunction()non hanno idea se SomeObject(o solo someFunctionper quella materia) sono archiviati ed eseguiti localmente o memorizzati ed eseguiti in remoto. La teoria dice che questo rende la programmazione più semplice.

La realtà è diversa perché c'è un'ENORME differenza tra fare una chiamata di funzione locale (anche se stai usando il linguaggio interpretato più lento del mondo) e:

  • chiamando tramite un oggetto proxy
  • serializzare i parametri
  • effettuare una connessione di rete (se non già stabilita)
  • trasmettere i dati al proxy remoto
  • fare in modo che il proxy remoto ripristini i dati e chiami la funzione remota per tuo conto
  • serializzare i valori restituiti
  • trasmettere i valori di ritorno al proxy locale
  • riassemblaggio dei dati serializzati
  • restituendo la risposta dalla funzione remota

Solo nel tempo si tratta di circa tre ordini (o più!) Di differenza di grandezza. Quei tre + ordini di grandezza faranno un'enorme differenza nelle prestazioni che renderà la tua astrazione di una chiamata di procedura trapelata piuttosto ovviamente la prima volta che consideri erroneamente un RPC come una vera chiamata di funzione. Inoltre una vera chiamata di funzione, salvo seri problemi nel codice, avrà pochissimi punti di errore al di fuori dei bug di implementazione. Una chiamata RPC presenta tutti i seguenti possibili problemi che verranno descritti come casi di errore oltre a quanto ci si aspetterebbe da una normale chiamata locale:

  • potresti non essere in grado di creare un'istanza del tuo proxy locale
  • potresti non essere in grado di creare un'istanza del proxy remoto
  • i proxy potrebbero non essere in grado di connettersi
  • i parametri inviati potrebbero non renderlo intatto o del tutto
  • il valore di ritorno che il telecomando invia potrebbe non renderlo intatto o del tutto

Quindi ora la tua chiamata RPC che è "proprio come una chiamata di funzione locale" ha un intero buttload di condizioni di errore extra con cui non devi fare i conti con le chiamate di funzione locali. L'astrazione è trapelata di nuovo, ancora più difficile.

Alla fine RPC è una cattiva astrazione perché perde come un setaccio ad ogni livello - quando ha successo e quando fallisce entrambi.


<pimp> Mi piace di più l'approccio Erlang a questo in quanto non cerca di nascondere la differenza tra una chiamata di funzione e l'invio di un messaggio a un processo al punto che i due usano una sintassi molto diversa. E l'invio di un messaggio di processo remoto è visibilmente diverso dall'invio di un processo locale, anche se utilizza la stessa sintassi generale. </pimp>
SOLO LA MIA OPINIONE corretta

2
Bene, questa è l'unica risposta che effettivamente dà un buon esempio (comprensione della lettura, gente), quindi ottiene il mio +1.
Mark E. Haase

4

Un esempio nell'esempio molti-a-molti di django ORM :

Notare nell'utilizzo dell'API di esempio che è necessario salvare () l'oggetto Article di base a1 prima di poter aggiungere oggetti Publication all'attributo many-to-many. E si noti che l'aggiornamento dell'attributo molti-a-molti salva immediatamente nel database sottostante, mentre l'aggiornamento di un attributo singolare non si riflette nel db fino a quando non viene chiamato .save ().

L'astrazione è che stiamo lavorando con un oggetto grafico, dove gli attributi a valore singolo e gli attributi a più valori sono solo attributi. Ma l'implementazione come archivio dati supportato da un database relazionale perde ... poiché il sistema di integrità dell'RDBS appare attraverso la sottile patina di un'interfaccia a oggetti.


3

Cos'è l'astrazione?

L'astrazione è un modo per semplificare il mondo. Significa che non devi preoccuparti di ciò che sta realmente accadendo sotto il cofano o dietro le quinte. Significa che qualcosa è a prova di idiota.

Esempio di astrazione: le complessità del volo con un 737/747 vengono "astratte"

Gli aerei sono macchinari molto complicati. Hai motori a reazione, sistemi di ossigeno, sistemi elettrici, sistemi di carrelli di atterraggio ecc. Ma il pilota non deve preoccuparsi delle complessità del motore a reazione ... tutto ciò che è "estratto". Ciò significa che il pilota deve solo preoccuparsi di guidare l'aereo: a sinistra per andare a sinistra e a destra per andare a destra, tirare su per guadagnare quota e spingere verso il basso per scendere.

È abbastanza semplice ... in realtà ho mentito: controllare il volante è un po 'più complicato. In un mondo ideale, questa è l'unica cosa di cui il pilota dovrebbe essere preoccupato. Ma questo non è il caso nella vita reale: se voli su un aereo come una scimmia, senza alcuna reale comprensione di come funziona un aereo, o di nessuno dei dettagli di implementazione, allora probabilmente schianterai e ucciderai tutti a bordo.

Leaky Abstractions in 737 Esempio

In realtà, un pilota deve preoccuparsi di MOLTE cose importanti - non tutto è stato astratto: i piloti devono preoccuparsi della velocità del vento, della spinta, degli angoli di attacco, del carburante, dell'altitudine, dei problemi meteorologici, degli angoli di discesa e se il pilota sta andando nella giusta direzione. I computer possono aiutare il pilota in queste attività, ma non tutto è automatizzato / semplificato.

Ad esempio, se il pilota si solleva troppo forte sulla colonna, l'aereo obbedirà, ma poi il pilota rischierà di fermare l'aereo e, una volta bloccato, è molto difficile riprenderne il controllo, prima che si schianti di nuovo a terra .

In altre parole, non è sufficiente che il pilota controlli semplicemente il volante senza sapere altro ......... nooooo ....... deve conoscere i rischi e i limiti sottostanti dell'aereo prima di farne volare uno ....... deve sapere come funziona l'aereo e come vola l'aereo; deve conoscere i dettagli di implementazione ..... deve sapere che tirare su con troppa forza porterà ad uno stallo, o che un atterraggio troppo ripido distruggerà l'aereo.

Quelle cose non vengono astratte. Molte cose vengono astratte, ma non tutto. Il pilota deve preoccuparsi solo del piantone dello sterzo e forse di una o due altre cose. L'astrazione è "che perde".

Perdite di astrazioni nel codice

...... è la stessa cosa nel tuo codice. Se non conosci i dettagli di implementazione sottostanti, il più delle volte ti ritroverai in un angolo.

Ecco un esempio di codifica:

Gli ORM astraggono gran parte della seccatura nel gestire le query sul database, ma se hai mai fatto qualcosa come:

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

Allora ti renderai conto che è un bel modo per uccidere la tua app se hai più di un paio di milioni di utenti. Non tutto è astratto. Devi sapere che le chiamate User.allcon 25 milioni di utenti aumenteranno l'utilizzo della memoria e causeranno problemi. Hai bisogno di conoscere alcuni dettagli di fondo. L'astrazione perde.


0

Il fatto che a un certo punto , che sarà guidato dalla tua scala e dalla tua esecuzione, sarà necessario acquisire familiarità con i dettagli di implementazione del tuo framework di astrazione per capire perché si comporta in quel modo.

Ad esempio, considera questa SQLquery:

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

E la sua alternativa:

SELECT * FROM student_details;

Ora, sembrano soluzioni logicamente equivalenti, ma le prestazioni della prima sono migliori grazie alla specifica dei nomi delle singole colonne.

È un esempio banale, ma alla fine torna alla citazione di Joel Spolsky:

Tutte le astrazioni non banali, in una certa misura, sono falle.

Ad un certo punto, quando raggiungerai una certa scala nella tua operazione, vorrai ottimizzare il modo in cui funziona il tuo DB (SQL). Per farlo, dovrai conoscere il modo in cui funzionano i database relazionali. All'inizio ti è stato astratto, ma perde. Devi impararlo ad un certo punto.


-1

Supponiamo di avere il seguente codice in una libreria:

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. 
}

Quando il consumatore chiama l'API, ottiene un oggetto []. Il consumatore deve capire che il primo campo della matrice di oggetti ha il valore del colore e il secondo campo è il valore del modello. Qui l'astrazione è trapelata dalla libreria al codice consumer.

Una delle soluzioni è restituire un oggetto che incapsula il modello e il colore del dispositivo. Il consumatore può chiamare quell'oggetto per ottenere il modello e il valore del colore.

DeviceColorAndModel fetchDeviceColorAndModel(String serialNumberOfTheDevice)
{
    //fetch Device Color and Device Model from DB.
    return new DeviceColorAndModel(color, model);
}

-3

L'astrazione che perde è tutta incapsulata nello stato. esempio molto semplice di astrazione leaky:

$currentTime = new DateTime();

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

class BankAccount {
    // ...

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

e il modo giusto (astrazione non leaky):

class BankAccount
{
    // ...

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

più descrizione qui .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.