Significado de Abstração Vazada?


90

O que significa o termo "Abstração Leaky"? (Explique com exemplos. Muitas vezes tenho dificuldade em grocar uma mera teoria.)



14
Você pode querer ler o artigo original de Joel Spolsky, The Law of Leaky Abstractions, que, pelo que eu sei, é a origem do termo.
Daniel Pryden,

2
A maioria das respostas do dupe proposto é sobre interfaces fluentes.
David Thornley,

@David: A segunda postagem mais votada responde o que significa abstração vazada, e com um ótimo exemplo.
missingfaktor,

4
Quando você procura esta pergunta no Google 4 anos depois, é difícil adivinhar qual postagem costumava ser a segunda mais votada.
John Reynolds

Respostas:


108

Aqui está um exemplo de espaço para carnes :

Os automóveis têm abstrações para os motoristas. Em sua forma mais pura, há volante, acelerador e freio. Essa abstração esconde muitos detalhes sobre o que está sob o capô: motor, cames, correia dentada, velas de ignição, radiador, etc.

O legal dessa abstração é que podemos substituir partes da implementação por partes aprimoradas sem treinar novamente o usuário. Digamos que substituamos a tampa do distribuidor por ignição eletrônica e substituamos o came fixo por um variável. Essas alterações melhoram o desempenho, mas o usuário ainda dirige com o volante e usa os pedais para iniciar e parar.

É realmente notável ... uma pessoa de 16 ou 80 anos pode operar essa máquina complicada sem realmente saber muito sobre como ela funciona por dentro!

Mas existem vazamentos. A transmissão é um pequeno vazamento. Em uma transmissão automática, você pode sentir o carro perder potência por um momento enquanto muda de marcha, enquanto na CVT você sente um torque suave em toda a direção.

Existem vazamentos maiores também. Se você acelerar o motor muito rápido, você pode danificá-lo. Se o bloco do motor estiver muito frio, o carro pode não dar a partida ou pode ter um desempenho ruim. E se você ligar o rádio, os faróis e o ar-condicionado ao mesmo tempo, verá seu consumo de combustível diminuir.


8
Obrigado pelo exemplo. Ninguém mais parecia ser capaz de fornecer uma explicação simples.
Sebastian Patten

8
Esta é uma ótima resposta, principalmente porque demonstra o ponto de vista do usuário, que é o que a versão do software tem a ver.
chad

1
O que significa espaço para carnes? Explicação do leigo?
brumScouse

1
@brumScouse "meatspace" significa o mundo físico offline. É usado para contrastar com o mundo online do ciberespaço. Vou editar minha resposta para incluir um link para a definição.
Mark E. Haase

1
Gosto de como este post aponta que "ainda há vazamentos". É tudo uma questão de minimizá-los.
alaboudi

52

Significa simplesmente que sua abstração expõe alguns dos detalhes de implementação ou que você precisa estar ciente dos detalhes de implementação ao usar a abstração. O termo é atribuído a Joel Spolsky , por volta de 2002. Consulte o artigo da wikipedia para obter mais informações.

Um exemplo clássico são as bibliotecas de rede que permitem tratar arquivos remotos como locais. O desenvolvedor que usa essa abstração deve estar ciente de que problemas de rede podem fazer com que isso falhe de maneiras que os arquivos locais não fazem. Em seguida, você precisa desenvolver código para lidar especificamente com erros fora da abstração que a biblioteca de rede fornece.


7
@mehaase Não vejo como importa se sua abstração vaza por design ou por negligência. Expandi a resposta com um exemplo e mais informações do artigo referenciado para que ele possa se sustentar sozinho. Além disso, não acho que "abstração vazada" necessariamente precise ser pejorativa. Para mim, apenas descreve uma situação em que você, como desenvolvedor, precisa ser mais cuidadoso ao trabalhar com a abstração. O design pode ser bom, ruim ou indiferente, independente do "vazamento".
tvanfosson

13

A Wikipedia tem uma definição muito boa para isso

Uma abstração com vazamento refere-se a qualquer abstração implementada, destinada a reduzir (ou ocultar) a complexidade, onde os detalhes subjacentes não estão completamente ocultos

Ou em outras palavras, para software, é quando você pode observar detalhes de implementação de um recurso por meio de limitações ou efeitos colaterais no programa.

Um exemplo rápido seria fechamentos C # / VB.Net e sua incapacidade de capturar parâmetros ref / out. A razão pela qual eles não podem ser capturados é devido a um detalhe de implementação de como o processo de levantamento ocorre. Isso não quer dizer que haja uma maneira melhor de fazer isso.


13

Aqui está um exemplo familiar aos desenvolvedores .NET: A Pageclasse ASP.NET tenta ocultar os detalhes das operações HTTP, particularmente o gerenciamento de dados de formulário, para que os desenvolvedores não tenham que lidar com valores postados (porque mapeia automaticamente os valores de formulário para o servidor controles).

Mas se você vagar além dos cenários de uso mais básicos, a Pageabstração começa a vazar e se torna difícil trabalhar com as páginas, a menos que você entenda os detalhes de implementação da classe.

Um exemplo comum é adicionar controles dinamicamente a uma página - o valor dos controles adicionados dinamicamente não será mapeado para você, a menos que você os adicione no momento certo : antes que o mecanismo subjacente mapeie os valores do formulário de entrada para os controles apropriados. Quando você precisa aprender isso, a abstração vazou .


Os formulários da web tinham bo bottom em seu balde. O pior é que as abstrações veladas equivaliam a trabalhar com o Http como se você estivesse trabalhando em um porta-luvas.
brumScouse

10

Bem, de certa forma, é uma coisa puramente teórica, embora não sem importância.

Usamos abstrações para tornar as coisas mais fáceis de compreender. Posso operar em uma classe de string em alguma linguagem para esconder o fato de que estou lidando com um conjunto ordenado de caracteres que são itens individuais. Eu lido com um conjunto ordenado de caracteres para esconder o fato de que estou lidando com números. Eu lido com números para esconder o fato de que estou lidando com 1s e 0s.

Uma abstração que vaza é aquela que não esconde os detalhes que deve ocultar. Se chamar string.Length em uma string de 5 caracteres em Java ou .NET, eu poderia obter qualquer resposta de 5 a 10, por causa dos detalhes de implementação onde o que essas linguagens chamam de caracteres são na verdade pontos de dados UTF-16 que podem representar 1 ou .5 de um personagem. A abstração vazou. No entanto, não vazar significa que encontrar o comprimento exigiria mais espaço de armazenamento (para armazenar o comprimento real) ou mudar de O (1) para O (n) (para descobrir qual é o comprimento real). Se eu me importo com a resposta real (muitas vezes você não se importa), você precisa trabalhar no conhecimento do que realmente está acontecendo.

Casos mais discutíveis acontecem com casos como quando um método ou propriedade permite que você entre no funcionamento interno, sejam eles vazamentos de abstração ou maneiras bem definidas de mover para um nível inferior de abstração, às vezes pode ser uma questão sobre a qual as pessoas discordam.


2
E você trabalha com 1 e 0 para esconder o fato de que está trabalhando com eletrônica e física (comentário muito tardio, eu sei)
Davy8

7

Vou continuar dando exemplos usando RPC.

No mundo ideal do RPC, uma chamada de procedimento remoto deve se parecer com uma chamada de procedimento local (ou assim a história continua). Deve ser completamente transparente para o programador, de forma que, ao chamar, SomeObject.someFunction()ele não tenha ideia se SomeObject(ou apenas someFunctionnesse caso) estão armazenados e executados localmente ou armazenados e executados remotamente. A teoria diz que isso torna a programação mais simples.

A realidade é diferente porque há uma diferença ENORME entre fazer uma chamada de função local (mesmo se você estiver usando a linguagem interpretada mais lenta do mundo) e:

  • chamando por meio de um objeto proxy
  • serializando seus parâmetros
  • fazer uma conexão de rede (se ainda não estiver estabelecida)
  • transmitindo os dados para o proxy remoto
  • fazer com que o proxy remoto restaure os dados e chame a função remota em seu nome
  • serializando o (s) valor (es) de retorno
  • transmitindo os valores de retorno para o proxy local
  • remontando os dados serializados
  • retornando a resposta da função remota

Só no tempo são três ordens (ou mais!) De diferença de magnitude. Essas três ordens de magnitude farão uma enorme diferença no desempenho, o que fará com que sua abstração de um vazamento de chamada de procedimento seja bastante óbvia na primeira vez que você tratar erroneamente um RPC como uma chamada de função real. Além disso, uma chamada de função real, exceto problemas graves em seu código, terá muito poucos pontos de falha fora dos bugs de implementação. Uma chamada RPC tem todos os seguintes problemas possíveis que serão tratados como casos de falha além do que você esperaria de uma chamada local normal:

  • você pode não conseguir instanciar seu proxy local
  • talvez você não consiga instanciar seu proxy remoto
  • os proxies podem não ser capazes de se conectar
  • os parâmetros que você envia podem não torná-lo intacto ou de todo
  • o valor de retorno que o remoto envia pode não torná-lo intacto ou de todo

Portanto, agora sua chamada RPC que é "exatamente como uma chamada de função local" tem um monte de condições de falha extras que você não precisa enfrentar ao fazer chamadas de função locais. A abstração vazou novamente, ainda mais forte.

No final das contas, o RPC é uma abstração ruim porque vaza como uma peneira em todos os níveis - quando bem-sucedido e quando falha em ambos.


<pimp> Eu gosto mais da abordagem Erlang para isso, pois ela não tenta esconder a diferença entre uma chamada de função e o envio de uma mensagem para um processo a ponto de os dois usarem uma sintaxe muito diferente. E um envio de mensagem de processo remoto é visivelmente diferente de um envio de processo local, embora use a mesma sintaxe geral. </pimp>
APENAS MINHA OPINIÃO correta

2
Bem, esta é a única resposta que realmente dá um bom exemplo (compreensão de leitura, pessoal), então ela recebe meu +1.
Mark E. Haase

4

Um exemplo no exemplo django ORM muitos para muitos :

Observe no Exemplo de uso da API que você precisa salvar () o objeto de artigo base a1 antes de adicionar objetos de publicação ao atributo muitos para muitos. E observe que atualizar o atributo muitos para muitos salva no banco de dados subjacente imediatamente, enquanto a atualização de um atributo único não é refletido no banco de dados até que o .save () seja chamado.

A abstração é que estamos trabalhando com um gráfico de objeto, onde atributos de valor único e atributos de valor múltiplo são apenas atributos. Mas a implementação como um banco de dados relacional com base no armazenamento de dados vaza ... à medida que o sistema de integridade do RDBS aparece através da fina camada de uma interface de objeto.


3

O que é abstração?

A abstração é uma forma de simplificar o mundo. Isso significa que você não precisa se preocupar com o que realmente está acontecendo sob o capô ou atrás da cortina. Isso significa que algo é à prova de idiotas.

Exemplo de abstração: as complexidades de voar um 737/747 são "abstraídas"

Os aviões são peças de maquinaria muito complicadas. Você tem motores a jato, sistemas de oxigênio, sistemas elétricos, sistemas de trem de pouso etc., mas o piloto não precisa se preocupar com as complexidades do motor a jato ... tudo isso é "abstraído". Isso significa que o piloto só precisa se preocupar em dirigir o avião: esquerda para ir para a esquerda e direita para ir para a direita, suba para ganhar elevação e empurre para baixo para descer.

É bastante simples ... na verdade eu menti: controlar o volante é um pouco mais complicado. Em um mundo ideal, essa é a única coisa com a qual o piloto deve se preocupar. Mas este não é o caso na vida real: se você pilotar um avião como um macaco, sem nenhuma compreensão real de como um avião opera, ou de qualquer dos detalhes de implementação, então provavelmente você cairá e matará todos a bordo.

Abstrações vazadas no exemplo 737

Na realidade, um piloto precisa se preocupar com MUITAS coisas importantes - nem tudo foi abstraído: os pilotos precisam se preocupar com a velocidade do vento, empuxo, ângulos de ataque, combustível, altitude, problemas climáticos, ângulos de descida e se o piloto está indo na direção certa. Os computadores podem ajudar o piloto nessas tarefas, mas nem tudo é automatizado / simplificado.

por exemplo, se o piloto puxar a coluna com muita força - o avião obedecerá, mas então o piloto corre o risco de estolar o avião e, uma vez estagnado, é extremamente difícil recuperar o controle dele, antes que ele caia de volta no chão .

Em outras palavras, não é suficiente para o piloto simplesmente controlar o volante sem saber mais nada ......... nãããão ... ele deve saber sobre os riscos e limitações subjacentes do avião antes de voar ... ele deve saber como o avião funciona e como ele voa; ele deve saber os detalhes da implementação ... ele deve saber que puxar com muita força levará a um estol, ou que pousar muito abruptamente destruirá o avião.

Essas coisas não são abstraídas. Muitas coisas são abstraídas, mas nem tudo. O piloto só precisa se preocupar com a coluna de direção e talvez com uma ou duas outras coisas. A abstração é "furada".

Abstrações vazadas no código

...... é a mesma coisa em seu código. Se você não conhece os detalhes de implementação subjacentes, então, na maioria das vezes, você ficará em apuros.

Aqui está um exemplo de codificação:

ORMs abstraem muito o incômodo de lidar com consultas de banco de dados, mas se você já fez algo como:

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

Então você perceberá que é uma boa maneira de eliminar seu aplicativo se você tiver mais de alguns milhões de usuários. Nem tudo é abstraído. Você precisa saber que fazer chamadas User.allpara 25 milhões de usuários aumentará o uso de memória e causará problemas. Você precisa saber alguns detalhes subjacentes. A abstração vaza.


0

O fato de que em algum ponto , que será guiado por sua escala e execução, você precisará se familiarizar com os detalhes de implementação de sua estrutura de abstração para entender por que ela se comporta dessa maneira.

Por exemplo, considere esta SQLconsulta:

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

E sua alternativa:

SELECT * FROM student_details;

Agora, eles parecem soluções logicamente equivalentes, mas o desempenho do primeiro é melhor devido à especificação de nomes de colunas individuais.

É um exemplo trivial, mas eventualmente ele retorna à citação de Joel Spolsky:

Todas as abstrações não triviais, até certo ponto, são vazias.

Em algum momento, quando você atingir uma determinada escala em sua operação, você desejará otimizar a maneira como seu BD (SQL) funciona. Para fazer isso, você precisará saber como funcionam os bancos de dados relacionais. Foi abstraído para você no início, mas está vazando. Você precisa aprender isso em algum momento.


-1

Suponha que temos o seguinte código em uma biblioteca:

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 o consumidor chama a API, ele obtém um Object []. O consumidor deve entender que o primeiro campo da matriz de objetos possui valor de cor e o segundo campo é o valor do modelo. Aqui, a abstração vazou da biblioteca para o código do consumidor.

Uma das soluções é retornar um objeto que encapsula o Modelo e a Cor do Dispositivo. O consumidor pode chamar esse objeto para obter o modelo e o valor da cor.

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

-3

A abstração Leaky tem tudo a ver com o estado de encapsulamento. exemplo muito simples de abstração com vazamento:

$currentTime = new DateTime();

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

class BankAccount {
    // ...

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

e da maneira certa (não abstração vazada):

class BankAccount
{
    // ...

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

mais descrição aqui .

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.