Qual é o valor em ocultar os detalhes através de abstrações? Não existe valor na transparência?

30

fundo

Eu não sou um grande fã de abstração. Admito que alguém possa se beneficiar da adaptabilidade, portabilidade e reutilização de interfaces, etc. Há um benefício real lá, e eu não quero questionar isso, então vamos ignorá-lo.

Há o outro "benefício" principal da abstração, que é ocultar a lógica de implementação e os detalhes dos usuários dessa abstração. O argumento é que você não precisa conhecer os detalhes e, nesse momento, deve-se concentrar em sua própria lógica. Faz sentido na teoria.

No entanto, sempre que tenho mantido grandes aplicativos corporativos, sempre preciso saber mais detalhes. Torna-se um enorme aborrecimento, aprofundando cada vez mais a abstração para descobrir exatamente o que algo faz; ou seja, ter que fazer "declaração aberta" cerca de 12 vezes antes de encontrar o procedimento armazenado usado.

Essa mentalidade de "ocultar os detalhes" parece apenas atrapalhar. Estou sempre desejando interfaces mais transparentes e menos abstração. Eu posso ler código fonte de alto nível e saber o que ele faz, mas nunca vou saber como ele faz, quando ele faz, é o que eu realmente preciso saber.

Oque esta acontecendo aqui? Todos os sistemas em que trabalhei foram mal projetados (pelo menos dessa perspectiva)?

Minha filosofia

Quando desenvolvo software, sinto que tento seguir uma filosofia que sinto estar intimamente relacionada à filosofia do ArchLinux :

O Arch Linux mantém as complexidades inerentes a um sistema GNU / Linux, mantendo-as bem organizadas e transparentes. Os desenvolvedores e usuários do Arch Linux acreditam que tentar ocultar as complexidades de um sistema realmente resulta em um sistema ainda mais complexo e, portanto, deve ser evitado.

E, portanto, nunca tento ocultar a complexidade do meu software atrás das camadas de abstração. Eu tento abusar da abstração, não me tornar escravo dela.

Pergunta no coração

  1. Existe um valor real em ocultar os detalhes?
  2. Não estamos sacrificando a transparência?
  3. Essa transparência não é valiosa?
user606723
fonte
7
A abstração pode ser abusada na forma de design incorreto. Mas isso não significa que a abstração em princípio não seja valiosa.
Bernard
4
Eu acho que há uma boa pergunta lá, no entanto, ela se parece muito com um discurso retórico contra a abstração. Você pode enfatizar isso e trazer à tona sua pergunta real mais.
PersonalNexus
4
Tem certeza de que está usando a definição correta de "ocultar os detalhes"? Nesse contexto, trata-se de reduzir o acoplamento , não de impedir que você aprenda o funcionamento interno de alguma coisa.
22712 Andres F.
24
A menos que você goste de programar com um voltímetro e osciliscópio em sua mesa, você está programando contra nada além de abstrações em cima de abstrações em cima de abstrações. Existe algum valor em ocultar os detalhes de que você está realmente manipulando não bits, mas de fato tensões? Isso sacrifica a transparência? Essa transparência é valiosa?
22812 Eric Lippert
8
Acho que você está tendo problemas com a abstração, são camadas vazias de indireção que realmente não abstraem nada. Sim, esses são freqüentemente encontrados em sistemas corporativos de grande porte e não, eles não são bons.
Michael Borgwardt

Respostas:

47

O motivo para ocultar os detalhes não é mantê-los ocultos; é possibilitar a modificação da implementação sem quebrar o código dependente.

Imagine que você tenha uma lista de objetos e cada objeto tenha uma Namepropriedade e outros dados. E muitas vezes, você precisa encontrar um item na lista que Namecorresponda a uma determinada string.

A maneira óbvia é fazer um loop sobre cada item, um por um, e verificar se Namecorresponde à sequência. Mas se você achar que isso está demorando muito (como faria se tivesse vários milhares de itens na lista), convém substituí-lo por uma pesquisa no dicionário de objetos de string.

Agora, se todas as suas pesquisas foram feitas recuperando a lista e fazendo um loop sobre ela, você tem uma enorme quantidade de trabalho a fazer para corrigir isso. É ainda mais difícil se você estiver em uma biblioteca e se forem usados ​​por terceiros; você não pode sair e consertar o código deles !

Mas se você tivesse um FindByNamemétodo para encapsular o processo de pesquisa de nome, você pode simplesmente mudar a maneira como ele é implementado e todo o código que o chama continuará funcionando, e ficará muito mais rápido gratuitamente. Esse é o valor real da abstração e encapsulamento.

Mason Wheeler
fonte
Eu concordo com isso, mas esse não era o ponto da questão. Posso concordar completamente que um método para encapsular funcionalidades específicas é útil, mas acho que esse nem sempre é o motivo. Estou errado?
user606723
3
@ User606723: Deveria ser. Isso não significa que sempre é . Algumas pessoas não entendem o ponto e simplesmente empilham mais camadas sobre outras e transformam as coisas em uma bagunça. É por isso que programadores experientes aconselham os desenvolvedores mais novos a nunca adotar uma nova tecnologia ou técnica até que eles entendam.
Mason Wheeler
3
@ user606723: A transparência incentiva o acoplamento apertado; portanto, embora talvez nem sempre seja ruim, mas geralmente é.
Malfist
3
O problema que Mason descreve, de pessoas empilhando camadas em uma forma de programação de cultos de carga, é o motivo pelo qual desconfio de muita herança e o motivo pelo qual a composição deve ser preferida à herança. Parece ser especialmente um problema para programadores Java.
jhocking
2
Não, o acoplamento apertado nem sempre é ruim. Geralmente é ruim, mas fazer o possível para evitá-lo pode produzir problemas ainda piores, como a codificação suave.
Mason Wheeler
16

Acabei de ler a seção no Código completo sobre abstração, e é aí que a maioria dessas fontes.

O objetivo da abstração é remover a necessidade de perguntar "como isso é implementado?". Quando você liga user.get_id(), você sabe que idé isso que você receberá de volta. Se você precisar perguntar "como isso obtém a identificação do usuário?" então você provavelmente não precisa de um id, ou get_id()retorna algo inesperado e mal projetado.

Você usa abstração para permitir que você crie:

a house with doors and windows

não projetar

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.
TorelTwiddler
fonte
Eu não estou falando sobre essas interfaces. Essas interfaces estão bem. Estou falando de enormes sistemas complexos que são segmentados por trás de muitas abstrações diferentes.
user606723
9
@ user606723 Então a pergunta é sobre design overcomplex, em vez de abstrações
Andres F.
3
+1 para o código completo. Ele abrange tanto por que a abstração é necessária para projetar quanto por que o nível errado de abstração dificulta o design. Para levar seu exemplo adiante, se eu estiver trabalhando no escritório de zoneamento, quero pensar em suas casas, sem mexer nos detalhes. Mas se você pensasse em casas como eu, não conseguiria construir uma.
Spencer Rathbun
esta questão deve ser encerrada ou o título alterado #
Jake Berger
8

Existe um valor real em ocultar os detalhes?

Sim. Ao apresentar abstrações, podemos pensar e programar em um nível superior.

Imagine modelar sistemas físicos sem cálculo ou álgebra matricial. É completamente impraticável. Da mesma forma, se pudermos apenas programar em nível escalar, não conseguiremos resolver problemas interessantes. Mesmo aplicativos da Web relativamente simples podem se beneficiar muito de abstrações como bibliotecas de tags. É muito mais fácil inserir uma tag que significa "campos de entrada de endereço" do que criar repetidamente quatro campos de texto e uma caixa de seleção. E se você decidir expandir para o exterior, poderá apenas modificar a definição de tag, em vez de corrigir todos os formulários para lidar com endereços internacionais. O uso eficaz da abstração é o que torna alguns programadores dez vezes mais eficazes que outros.

Os seres humanos têm uma memória de trabalho limitada. A abstração nos permite raciocinar sobre sistemas grandes.

Aren't we sacrificing transparency?

Não. Se abstrações não forem usadas, o objetivo de um componente de software será oculto em detalhes repetidos. Os desenvolvedores passam seus dias vasculhando códigos como este:

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

e pensando "oh sim, outro loop de quatro níveis sobre os kits", em vez de ver

pilgrim.kits.each { ++count; }

Essa transparência não é valiosa?

Como você apontou, há um custo para a indireção. Não faz sentido criar camadas "apenas por precaução". Use a abstração para reduzir a duplicação e esclarecer o código.

Kevin Cline
fonte
7

Quando as pessoas dizem que as abstrações ocultam os detalhes da implementação, na verdade não significam "ocultar" no sentido de dificultar a localização. O que eles querem dizer é separar os detalhes da implementação da interface pública, para manter a interface simples, concisa e gerenciável. Assim como um carro "esconde" a maior parte de suas partes vitais e oferece apenas um conjunto de controles bastante rudimentar para operá-los, um módulo de software "oculta" a maior parte de sua funcionalidade nas profundezas do intestino e expõe apenas um número limitado de métodos de acesso a eles. Dirija. Imagine um carro em que você tivesse que operar manualmente todos os componentes internos do motor (e há muitos deles), seria muito difícil manter um olho no tráfego e encontrar o caminho.

Mas manter a interface simples não é apenas uma coisa estética; pode fazer a diferença entre um projeto bem-sucedido e uma Marcha da Morte. Vamos bancar o advogado do diabo por um minuto; imagine um projeto de software sem abstrações. Se você precisar manter um valor, use uma variável global. Se você precisar usar a funcionalidade mais de uma vez, copie e cole. Se você precisar de duas versões diferentes de uma determinada seção de código, copie e cole, envolva-a em uma ifinstrução e modifique os dois ramos. Tecnicamente falando, funciona, mas alguns meses depois, você estará enfrentando alguns problemas realmente desagradáveis:

  • Quando você encontra e corrige um bug, é provável que ele exista também em outras instâncias copiadas e coladas de código semelhante; portanto, além de encontrar e corrigir o bug, você também precisa procurar outras ocorrências e corrigi-las também.
  • Para encontrar um bug ou implementar uma alteração, um programador de manutenção deve ser capaz de entender o código relevante. A dificuldade de fazer isso aumenta com o tamanho da seção de código relevante, mas ainda mais com seu escopo. Manter meia dúzia de variáveis ​​em sua cabeça enquanto passo mentalmente por algum código é factível; mas se você tiver algumas centenas delas, sua produtividade será fortemente afetada (eu gosto de comparar o processo de pensamento com um programa que fica sem memória RAM física e precisa mergulhar no arquivo de troca: em vez de ler o código fluentemente de uma só vez , o programador precisa ir e voltar para procurar coisas).
  • O escopo de um trecho de código também afeta o tamanho da base de código que é preciso pesquisar para encontrar o bug. Se você possui uma função de dez linhas com dois parâmetros, e não globais, e conhece os valores da entrada e a linha na qual ela trava, encontrar o bug geralmente é trivial e geralmente requer nada mais do que examinar o código. Se são algumas centenas de linhas, vinte parâmetros, quinze globais e chama algumas outras funções de natureza semelhante, você está sofrendo sérias dores.
  • Sem uma abstração adequada, qualquer alteração pode afetar potencialmente grandes partes da base de código, pois praticamente tudo pode depender do código a ser alterado. Um sintoma típico dessas bases de código é que você faz uma alteração pequena e aparentemente inocente, e um recurso completamente não relacionado se quebra repentinamente. Com a abstração, você pode limitar a quantidade de dano que uma alteração pode causar e tornar o impacto mais previsível. Se você alterar o nome de um campo particular, você terá apenas um arquivo de origem para verificar; se você alterar o nome de uma variável global, precisará percorrer toda a base de código.

Em uma base de código mal abstraída, o impacto geralmente aumenta exponencialmente com o tamanho da base de código, ou seja, adicionar uma quantidade constante de código aumenta o esforço de manutenção por um fator constante. Para piorar a situação, adicionar mais programadores a um projeto não aumenta a produtividade linearmente, mas logaritmicamente na melhor das hipóteses (porque quanto maior sua equipe, mais sobrecarga é necessária para a comunicação).

tdammers
fonte
2

Eu acho que você deve entender como funciona, se necessário. Depois de determinar que ele faz o que você pensou que faria, então você fica tranquilo. Eu nunca pensei que o objetivo era escondê-lo para todo o sempre.

Depois de definir um alarme em um relógio que você acredita que funcionará, você pode dormir um pouco sabendo que ele disparará no horário correto. Acordar uma hora mais cedo só para ver os segundos passarem é um desperdício.

JeffO
fonte
11
Claro, mas não me pedem frequentemente que altere o funcionamento do meu despertador ou que outras pessoas alterem o funcionamento do meu despertador sem estar totalmente informado das alterações.
user606723
11
Você vê o código alterado para cada estrutura que você já usou?
Jeffo
11
não, mas não tenho alterações de código para todas as estruturas que já usei.
user606723
3
Você já provou o ponto de vista de JeffO: você também não está no negócio de manter despertadores. Se você comprar um e começar a usá-lo sem fazer uma análise completa de como funciona, você aceitou que o interior será abstrato para você. Nada diz que você não pode separá-lo para descobrir como funciona, mas com que frequência você acha necessário?
Blrfl
2

Para responder às suas perguntas especificamente:

Existe um valor real em ocultar os detalhes?

Sim. Como você reconhece na primeira linha da sua pergunta.

Não estamos sacrificando a transparência?

Na verdade não. Uma abstração bem escrita facilitará a compreensão dos detalhes, se necessário.

Essa transparência não é valiosa?

Sim. As abstrações devem ser projetadas e implementadas para facilitar a compreensão dos detalhes quando necessário / desejado.

Craig
fonte
Finalmente uma resposta que eu gosto.
user606723
1

Eu diria que esconder os detalhes é ótimo quando as coisas ocultas funcionam.

Por exemplo, digamos que desenvolvamos uma interface que define comportamentos (por exemplo, GetListItems, SendListItem), que são dois recursos iniciados pelo usuário com um clique de botão ou algo assim. AGORA, cada usuário pode ter seu próprio "ListItemStore". está no facebook, no myspace .. (por exemplo) .. e diz que é salvo como uma propriedade / configuração do usuário em algum lugar no aplicativo por meio de preferências do usuário. e diz que é possível para os desenvolvedores do aplicativo adicionar ListItemStore adicionais ao longo de time (mybook, facespace, etc.)

agora há muitos detalhes na conexão com o facebook e na obtenção de itens ... e há tantos detalhes ao se conectar ao myspace ... e assim por diante ...

agora, depois que o código inicial de "acesso à loja" é gravado, ele pode não precisar ser modificado (no caso dos facebooks, provavelmente precisamos de um desenvolvedor em tempo integral para acompanhar as alterações, zing ..),

então, quando você usa o código, é algo como:

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

e agora você tem os dados do usuário de onde quer que eles estejam armazenados, e como tudo o que me preocupa é obter a lista de itens e fazer alguma coisa com ele, e já que foram necessárias apenas duas linhas, que funcionam, não importa como muitas outras lojas são adicionadas, posso voltar a responder / publicar perguntas na pilha ... lol ..

Então, todo o encanamento para que isso aconteça é "oculto" e quem realmente se importa com o que faz, desde que eu receba a lista correta de itens. Se você tiver testes de unidade, pode ficar ainda mais fácil porque os resultados devem " já foi quantificado ..

hanzolo
fonte
Sim, mas a parte que precisa ser mantida nunca é essas duas linhas. São impossíveis de estragar. É sempre o segundo, terceiro, quarto nível abaixo que você precisa alterar. Concordo que isso é ótimo quando você tem uma API em que pode confiar para ser estável , mas e se ela não puder ser estável apenas pela complexidade do seu negócio?
user606723
11
@ user606723 se API não é estável, então é provável que seja imaturo, ou mais provavelmente a abstração errado
SDG
@ user606723 - Isso é verdade e, nesses casos, as próprias convenções de nomenclatura devem definir alguma forma de transparência que oculte a lógica / detalhes reais de programação. portanto, em essência, a transparência pode ser alcançada com a nomeação adequada até o fim; no entanto, não precisamos realmente ir até o fim com muita frequência.
hanzolo
@sdg ou porque os requisitos do sistema estão sempre mudando. Como as leis mudam, porque as APIs dos outros mudam, está fora de nosso controle.
user606723
11
@ user606723 - na verdade, trabalho em um sistema SaaS financeiro e vejo as armadilhas de não ter abstração suficiente a cada ciclo de lançamento. parte disso é resultado de um design ruim, mas geralmente é o resultado de empurrar o código para onde não foi originalmente planejado. Descendo a cadeia pode ser doloroso sem esses comentários, nomes e encapsulamento . se tudo que eu tivesse que fazer fosse Remeasurements.GetRemeasureType (grant) e reMeasure.PerformCalc (), seria muito melhor do que adicionar sobrecargas e ler as diferentes ramificações lógicas para chegar ao cálculo adequado ou adicionar uma nova
hanzolo 26/01
1

O que você chama de "Ocultar", muitos vêem como uma separação de preocupações (por exemplo, Implementação vs. Interface).

Na minha opinião, um grande benefício da abstração é reduzir a confusão de detalhes desnecessários do espaço limitado do cérebro do desenvolvedor.

Se o código de implementação fosse ofuscado, eu poderia ver que isso dificulta a transparência, mas a abstração como a vejo é apenas uma boa organização.

Somantra
fonte
1

Primeiro, qualquer coisa além de uma única instrução de código de máquina é essencialmente abstração - um while...doloop é uma maneira simbólica consistente de representar as comparações e chamadas de endereço necessárias para repetir um conjunto de instruções até que uma condição seja atendida. Da mesma forma, o tipo int é uma abstração para o número X de bits (dependendo do seu sistema). A programação tem tudo a ver com abstração.

Você provavelmente concordaria que essas abstrações primitivas são muito úteis. Bem, assim é ser capaz de construir o seu próprio. OOAD e OOP têm tudo a ver.

Suponha que você tenha um requisito no qual os usuários possam exportar os dados de uma tela em vários formatos: texto delimitado, excel e pdf. Não é prático criar uma interface chamada "Exportador" com um método de exportação (dados), com base no qual você pode criar um DelimitedTextExporter, um ExcelExporter e um PDFExporter, cada um dos quais sabe como criar sua saída específica? Tudo o que o programa de chamada precisa saber é que ele pode chamar o método de exportação (dados) e qualquer implementação usada fará seu trabalho. Além disso, se as regras de texto delimitado forem alteradas, você poderá alterar o DelimitedTextExporter sem precisar mexer no ExcelExporter, possivelmente quebrando-o.

Praticamente todos os padrões de design conhecidos usados ​​na programação OO dependem da abstração. Eu recomendo a leitura de Freeman e Freeman's Head First Design Patterns para ter uma idéia melhor de por que a abstração é uma coisa boa

Matthew Flynn
fonte
11
até o código da máquina é uma abstração, existem processos reais e físicos acontecendo sob a lógica, até a eletrônica digital é uma abstração do que realmente acontece, como qualquer pessoa que tenha feito um hash de overclock de um chip possa testemunhar
jk.
É verdade, embora pareça mais concreto no nível de instrução da máquina.
Matthew Flynn
1

Acho que entendo sua opinião sobre isso e acho que tenho uma opinião semelhante.

Trabalhei com desenvolvedores Java que transformam 50 classes de linhas de código em 3 classes e 3 interfaces, porque é fácil de entender. E eu não aguentava.

A coisa era muito difícil de entender, quase impossível de depurar e nunca precisava "mudar a implementação".

Por outro lado, também vi código em que vários objetos compartilham comportamento semelhante e são usados ​​em um só lugar, e poderiam realmente usar loops comuns de classificação / processamento se os métodos tivessem sido expostos por meio da interface comum.

Assim, IMHO, objetos principais que provavelmente serão usados ​​em cenários semelhantes geralmente se beneficiam de comportamentos comuns que devem ser acessíveis através da interface. Mas é basicamente isso: abstrair coisas simples porque é certo ou possibilitar a troca de implementações é apenas uma maneira de tornar o código confuso.

Por outro lado, prefiro aulas mais inteligentes e longas do que uma quantidade explosiva de turmas pequenas com todos os problemas de gerenciamento da vida, relacionamentos difíceis de ver e gráficos de chamadas de espaguete. Então, algumas pessoas vão discordar de mim.

Codificador
fonte
1

O objetivo norteador de ocultar e abstração deve ser dissociar o usuário da implementação para que ele possa ser alterado de forma independente. Se o consumidor estiver acoplado aos detalhes da implementação, devido à brincadeira com os internos, ambos são lançados na pedra e fica mais difícil introduzir novos recursos. ou algoritmos melhores no futuro.

Ao escrever um módulo, partes ocultas da implementação oferecem a você a capacidade de alterá-las sem o risco de quebrar outro código que você não consegue imaginar.

Outra vantagem de fornecer interfaces opacas é que elas reduzem significativamente a área de superfície entre os subsistemas. Ao reduzir a quantidade de maneiras pelas quais eles podem interagir, eles podem se tornar mais previsíveis, mais fáceis de testar e ter menos erros. As interações entre os módulos também aumentam quadraticamente com o número de módulos, portanto, é importante tentar controlar esse crescimento da complexidade.


Dito isto, é claro que é possível ocultar demais e aninhar interfaces muito profundas. O trabalho do programador, como um ser humano inteligente, é projetar o sistema para que seja útil ao máximo, além de minimizar a complexidade e maximizar a manutenção.

hugomg
fonte
0

Em muitos casos, você simplesmente não precisa saber como as coisas são implementadas. Eu quase posso garantir que você escreverá um código como esse people.Where(p => p.Surname == "Smith")tantas vezes por dia, mas dificilmente pensará "como esse Where()método realmente funciona?" Você simplesmente não se importa - você sabe que esse método existe e que obtém os resultados desejados. Por que você se importaria como isso funciona?

É exatamente o mesmo para qualquer software interno; só porque não foi escrito pela Oracle, Microsoft, etc, não significa que você precise pesquisar sobre como ele é implementado. Você pode esperar razoavelmente que um método chamado GetAllPeopleWithSurname(string name)retorne uma lista de pessoas com esse sobrenome. Pode percorrer uma lista, pode usar um dicionário, pode fazer algo completamente louco, mas você não deve se importar .

Há uma exceção a essa regra, é claro (não seria uma regra sem uma!) E isso ocorre se houver um erro no método. Portanto, no exemplo acima, se você possui uma lista com três pessoas e sabe que uma delas tem o sobrenome Smith e elas não são retornadas na lista , você se preocupa com a implementação desse método, porque está claramente quebrado .

A abstração, quando feita corretamente, é maravilhosa porque permite filtrar todas as coisas que não são úteis quando você precisa lê-las posteriormente. Não se esqueça que gasta muito mais tempo lendo o código do que o escrevendo; portanto, a ênfase deve estar em tornar essa tarefa o mais fácil possível. Você também pode estar pensando que abstração significa uma hierarquia de objetos contanto que seu braço, mas pode ser tão simples quanto refatorar um método de 100 linhas em 10 métodos, cada um com 10 linhas. Então, o que antes era 10 etapas agrupadas agora é 10 etapas separadas, permitindo que você vá direto para o local onde esse bug traquina está escondido.

Stuart Leyland-Cole
fonte
Ah, mas digamos que você é quem mantém a implementação. Quero dizer, alguém se importa com a implementação, e essa pessoa é você. Você também é o usuário da implementação ... Os requisitos de negócios mudam (de maneiras que você não pode prever), causando mudanças que estão em várias camadas. Acho que meu argumento é que um bug não é a única exceção a essa regra.
user606723
O problema éPeopleFactory.People.Strategy.MakePeople.(CoutryLaw.NameRegistry.NameMaker.Make()) as People.Female
Coder
@ user606723 Você não precisa se preocupar com cada linha de código em sua base de código o tempo todo. Se houver um bug nessa implementação ou ele tiver sido marcado como precisando ser reescrito (porque é lento, mal escrito ou o que seja) e você estiver reescrevendo, então você se preocupa com isso. Caso contrário, deve ser mantido fora do caminho. Talvez o seu problema seja que você está tentando possuir todo o código o tempo todo. Você deve apenas se concentrar no código em que está trabalhando em um momento específico, na minha opinião.
Stuart Leyland-Cole
@ Codificador Obviamente, essa é uma forma extrema de abstração! No começo, pensei que era o tipo de coisa contra a qual o OP se opunha, mas ao ler o restante de suas respostas, parece que eles veem toda abstração como ruim, ruim, ruim. Por isso, tentei explicar os diferentes níveis de abstração em minha resposta.
Stuart Leyland-Cole
0

Abstrações resultam em ocultação de informações. Isso deve terminar no acoplamento inferior. Isso deve levar a menos riscos na mudança. Isso deve levar os programadores felizes, não ficando nervosos ao tocar no código.

Essas idéias são expressas através de três leis essenciais na arquitetura de software:

Lei de Simon: "Hierarquias reduzem a complexidade". (Hierarquias introduzem abstração)

Lei de Parna: "Somente o que está oculto pode ser alterado sem risco".

Lei de Constantin: programas robustos precisam de baixo acoplamento e alta coesão

ins0m
fonte
"Hierarquias reduzem a complexidade." - não necessariamente verdade.
Coder
Nenhuma metodologia de design é determinística. Esta lei não vem de TI / CS, é formulada em um sentido muito mais amplo e também é referida por matemática, físicos etc. É um princípio válido, mas ninguém pode impedir você de criar hierarquias sem sentido.
precisa saber é
0

Também estou no negócio de aplicativos corporativos e essa pergunta chamou minha atenção porque eu mesma tenho a mesma pergunta. Eu tive algumas idéias sobre a questão da abstração ao longo da minha carreira até agora, mas minha visão não é de modo algum a resposta universal. Continuo aprendendo / ouvindo novas idéias e pensamentos, então o que acredito agora pode mudar.

Quando eu mantinha uma aplicação de saúde grande e complexa, gostava de você, odiava todas as abstrações lá. Descobrir onde todo o código vai foi uma dor no pescoço. Pular em diferentes classes me deixou tonto. Então eu disse a mim mesmo "abstração é uma porcaria, vou minimizar a abstração quando criar coisas".

Então chegou a hora de projetar um aplicativo (pequeno componente de serviço da Web) a partir do zero. Lembrando de toda a dor, eu tinha um design bastante plano do componente. O problema era que, quando os requisitos foram alterados, tive que fazer alterações em muitos lugares diferentes (os requisitos eram bastante fluidos e não pude fazer nada sobre isso). Foi tão ruim que eu basicamente joguei fora meu design inicial e o redesenhei com abstração, e as coisas melhoraram - eu não precisei fazer alterações em muitos lugares quando os requisitos mudaram mais.

Entreguei o aplicativo, fiquei sentado por algumas semanas e fui orientado a começar a manter o aplicativo. Fazia um tempo, não me lembrava de tudo, então lutei um pouco para entender meu próprio código, e a abstração não estava ajudando.

Fui colocado em muitos outros projetos depois e tive a chance de brincar um pouco mais com os níveis de abstração. O que realmente acho é que, e essa é apenas minha opinião pessoal, a abstração ajuda muito no desenvolvimento, mas tem um impacto negativo quando você não escreve o código e está tentando entender tudo no nível mais profundo de um aplicativo; você passará mais tempo pulando em diferentes classes e tentando fazer as conexões.

Meu sentimento é que a abstração é tão valiosa durante o tempo de desenvolvimento que vale a pena o problema que enfrentamos como mantenedor ao tentar entender o código. Existem softwares para resolver problemas de negócios, os problemas de negócios evoluem com o tempo; portanto, o software precisa evoluir com o tempo. Sem abstração, evoluir o software é muito difícil. Pode-se projetar abstração de uma maneira que o mantenedor possa navegar facilmente pela base de código depois de ver o padrão da estrutura do código, de modo que apenas a curva de aprendizado inicial seja frustrante.

Alvin
fonte
0

Como outros já disseram, "ocultar detalhes" por trás de uma abstração permite que eles sejam alterados sem afetar os usuários. Esta idéia vem dos critérios de Parnas para serem usados ​​na decomposição de sistemas em módulos (1972) , , e está relacionada à idéia de tipos de dados abstratos (ADT) e programação orientada a objetos.

Ao mesmo tempo, o modelo relacional de dados do Codd para grandes bancos de dados compartilhados (1970) foi motivado (veja o resumo e a introdução) ao querer alterar a representação de armazenamento interno dos bancos de dados, sem afetar os usuários do banco de dados. Ele viu programadores regularmente levando dias, modificando páginas de código, para lidar com pequenas alterações de armazenamento.

Dito isto, uma abstração não é muito útil se você precisar ver o que está dentro dela para poder usá-la. Pode ser muito difícil projetá-lo bem. Um exemplo de uma boa abstração é a adição - quando foi a última vez que você teve que pensar no que acontece lá dentro? (mas às vezes você faz, por exemplo, para transbordamento).

O problema essencial (IMHO) é que, para projetar bem os módulos (no sentido de Parnas), você precisa prever o que vai mudar e o que não vai mudar. Prever o futuro é difícil - mas se você tiver muita experiência com algo e entender claramente, poderá fazer um bom trabalho de previsão. E, portanto, você pode criar um módulo (abstração) que funcione bem.

No entanto, parece o destino de todas as abstrações - mesmo as melhores - que eventualmente haverá mudanças imprevisíveis (e sem dúvida imprevisíveis) que exigem a quebra da abstração. Para resolver isso, algumas abstrações têm um escape, onde você pode obter acesso a um nível mais profundo, se realmente precisar.

Tudo isso parece muito negativo. Mas acho que a verdade é que estamos cercados por abstrações que funcionam tão bem que não as notamos ou percebemos o que estão escondendo. Observamos apenas as más abstrações, por isso temos uma visão icônica delas.

13ren
fonte
0

As abstrações são principalmente para o benefício de seus consumidores (por exemplo, programadores de aplicativos). Os programadores do sistema (designer) têm mais trabalho a fazer para torná-los bonitos e úteis, e é por isso que o bom design geralmente não é feito pelos iniciantes.

Talvez você não goste de abstrações porque elas sempre adicionam complexidade? Talvez os sistemas nos quais você trabalhou tenham abstração (uso excessivo de abstrações)? Eles não são uma panacéia.

O trabalho extra e a complexidade de uma abstração útil devem valer a pena, mas é difícil saber com certeza. Se você pensa em uma abstração como um ponto de articulação, o design do software pode ser flexível de ambos os lados: as implementações da abstração podem ser modificadas sem quebrar o código do cliente e / ou novos clientes podem reutilizar facilmente a abstração para criar coisas novas.

Você quase pode medir o retorno do investimento das abstrações, mostrando que elas foram "flexionadas" ao longo do tempo em uma ou em ambas as direções: mudanças relativamente simples na implementação e novos clientes adicionais.

Por exemplo: Usando a abstração da classe Socket em Java, tenho certeza de que meu código de aplicativo do Java 1.2 ainda funciona bem no Java 7 (embora possam haver algumas alterações no desempenho). Desde o Java 1.2, definitivamente existem muitos novos clientes que também usam essa abstração.

Quanto à infelicidade com abstrações, se eu conversasse com os desenvolvedores que mantiveram o código por trás da classe Socket, então talvez suas vidas não sejam tão pessimistas quanto os clientes que usaram o Socket para escrever aplicativos divertidos. Trabalhar na implementação de uma abstração certamente é mais trabalho do que usá-la. Mas isso não torna as coisas ruins.

Quanto à transparência, em uma estratégia de design de cima para baixo, a transparência total contribui para o mau design. Programadores inteligentes tendem a tirar o máximo proveito das informações que têm à sua disposição, e o sistema fica fortemente acoplado. A menor alteração de detalhe (por exemplo, alterar a ordem dos bytes em uma estrutura de dados) em um módulo pode quebrar algum código em outro lugar, porque um programador inteligente usa essas informações para fazer algo útil.David Parnas apontou esse problema em artigos tão antigos quanto 1971, onde ele propôs ocultar informações em projetos.

Sua referência ao ArchLinux faz sentido para mim se você considerar o "interior" do sistema operacional como a implementação complexa da abstração que é o sistema operacional para os aplicativos executados nele. Mantenha-o simples nas entranhas da abstração.

Fuhrmanator
fonte
0

Eu responderei sua pergunta com uma pergunta; quando você dirigiu para o trabalho hoje de manhã (presumo que o fez de fato), você se importou exatamente como o motor abriu as válvulas para permitir a entrada de misturas de combustível e ar e as acendeu? Não. Você não se importa com o funcionamento do motor do seu carro quando está dirigindo pela estrada. Você se importa que ele faz o trabalho.

Suponha que um dia seu carro não funcione. Não começa, joga uma vara, quebra um cinto, inexplicavelmente entra nessa barreira de concreto sem culpa sua enquanto você estava ocupado, enviando mensagens de texto. Agora, você precisa de um carro novo (pelo menos temporariamente). Você se importa exatamente como esse novo carro funciona? Não. O que mais lhe interessa é que ele funcione, e depois que você pode usar o mesmo conhecimento e habilidades que usou para dirigir seu carro antigo para dirigir o novo. Idealmente, deve parecer que não houve alteração no carro que você está dirigindo. Realisticamente, a maneira como este novo carro funciona deve lhe dar o mínimo de "surpresas" possível.

Esses princípios básicos são o princípio básico por trás do encapsulamento e da abstração. O conhecimento de como um objeto faz o que faz não deve ser um requisito para usá-lo para fazer o que faz. Mesmo na programação de computadores, os detalhes dos caminhos elétricos na CPU executando o programa são abstraídos por pelo menos meia dúzia de camadas de instruções de E / S, drivers, software do SO e tempo de execução. Muitos engenheiros de software bem-sucedidos escrevem um código perfeitamente bom sem se preocupar com a arquitetura exata de hardware ou com a compilação do sistema operacional que o executará. Incluindo eu.

O encapsulamento / ocultação de informações permite a mentalidade "não se importa com o que acontece, apenas se preocupa com isso". Seu objeto deve expor o que é útil para o consumidor, de uma maneira que ele possa consumir facilmente. Agora, de volta ao mundo real, isso não significa que um carro não deva fornecer ao usuário informações sobre o funcionamento interno ou que ele só deve permitir ao usuário as funcionalidades mais básicas, como ignição, volante, e pedais. Todos os carros têm velocímetros e indicadores de combustível, tacômetros, luzes idiotas e outros comentários. Praticamente todos os carros também possuem interruptores para vários subsistemas independentes, como faróis, piscas, rádio, ajuste de assentos, etc. Alguns carros permitem uma entrada bastante esotérica do usuário, como a sensibilidade do diferencial central de escorregamento limitado. Em todos os casos, se você souber o suficiente, você pode abri-lo e mudar as coisas para que funcione de uma maneira um pouco diferente. Mas, na maioria dos casos, talvez, apenas talvez, o usuário não consiga controlar direta e independentemente as bombas de combustível de dentro da cabine? Talvez, apenas talvez, o usuário não consiga ativar as luzes de freio sem pressionar o pedal do freio?

A abstração permite que "isso não é o mesmo que isso, mas, porque ambos são XI, podem usá-los como faria com qualquer X". Se seu objeto herda ou implementa uma abstração, seus consumidores devem esperar que sua implementação produza o mesmo resultado ou semelhante a outras implementações conhecidas da abstração. Um Toyota Camry e um Ford Fusion são "carros". Como tal, eles têm um conjunto comum de funcionalidades esperadas, como um volante. Gire no sentido anti-horário, o carro vai para a esquerda. Gire no sentido horário, o carro vai para a direita. Você pode entrar em qualquer carro nos Estados Unidos e esperar que o carro tenha um volante e pelo menos dois pedais, o da direita sendo o pedal "carro vai" e o do centro o pedal "carro pára" .

Um corolário da abstração é a "teoria do mínimo espanto". Se você pegasse o volante de um carro novo para um test drive, girasse o volante no sentido horário e o carro virasse para a esquerda, você ficaria surpreso em dizer o mínimo. Você acusaria o revendedor de vender um PDV e provavelmente não ouviria nenhuma das razões pelas quais o novo comportamento é "melhor" do que você está acostumado, ou quão bem esse comportamento é "documentado" ou como " transparente "o sistema de controle é. Apesar deste carro novo e de todos os outros que você dirigiu ainda serem "carros", ao dirigir esse carro, você precisa alterar alguns conceitos fundamentais de como um carro deve ser dirigido para dirigir com sucesso o novo carro. Isso normalmente é uma coisa ruim, e isso só acontece quando há uma vantagem intuitiva para o novo paradigma. Talvez a adição de cintos de segurança seja um bom exemplo; Há 50 anos, você entrou e foi embora, mas agora precisa apertar o cinto, a vantagem intuitiva é que você não entra no pára-brisa ou no banco do passageiro se sofrer um acidente. Mesmo assim, os motoristas resistiram; muitos proprietários de automóveis cortam os cintos de segurança do carro até que sejam aprovadas leis exigindo seu uso.

KeithS
fonte