O que é "abstração prematura"?

10

Eu ouvi a frase sendo lançada ao redor e para mim os argumentos parecem completamente insanos (desculpe se estou pensando aqui, não é minha intenção), geralmente isso é algo parecido com:

Você não deseja criar uma abstração antes de saber qual é o caso geral; caso contrário (1) você pode colocar coisas em suas abstrações que não pertencem ou (2) omitir coisas importantes.

(1) Para mim, isso parece que o programador não está sendo pragmático o suficiente, eles fizeram suposições de que coisas existiriam no programa final que não funcionam, então eles estão trabalhando com um nível baixo de abstração, o problema não é abstração prematura, é concreção prematura.

(2) Omitir coisas importantes é uma coisa, é perfeitamente possível que algo seja omitido das especificações que mais tarde se revelem importantes, a solução para isso não é apresentar sua própria concreção e desperdiçar recursos quando você descobrir que adivinhado errado, é para obter mais informações do cliente.

Devemos sempre trabalhar das abstrações às concreções, pois essa é a maneira mais pragmática de fazer as coisas, e não o contrário.

Se não o fizermos, corremos o risco de interpretar mal os clientes e criar coisas que precisam ser alteradas, mas se construirmos apenas as abstrações que os clientes definiram em seu próprio idioma, nunca correremos esse risco (pelo menos, nem de longe, com a probabilidade de um tiro no escuro com alguma concreção), sim, é possível que os clientes mudem de idéia sobre os detalhes, mas as abstrações que eles usavam para comunicar originalmente o que eles querem tendem a continuar válidas.

Aqui está um exemplo, digamos que um cliente deseja que você crie um robô de ensacamento de itens:

public abstract class BaggingRobot() {
    private Collection<Item> items;

    public abstract void bag(Item item);
}

Estamos construindo algo a partir das abstrações que o cliente usou sem entrar em mais detalhes com coisas que não sabemos. Isso é extremamente flexível, já vi isso ser chamado de "abstração prematura" quando, na realidade, seria mais prematuro assumir como a embalagem foi implementada, digamos, depois de discutir com o cliente, que eles querem que mais de um item seja ensacado de uma só vez . Para atualizar minha classe, tudo o que preciso é alterar a assinatura, mas para alguém que começou de baixo para cima, isso pode envolver uma grande revisão do sistema.

Não existe abstração prematura, apenas concretização prematura. O que há de errado com esta afirmação? Onde estão as falhas no meu raciocínio? Obrigado.

James
fonte
1
O oposto da abstração prematura é YAGNI - Você não vai precisar, o que, na minha opinião, quase sempre está errado. Quase. Eu já vi isso acontecer, mas é raro. Eu concordo principalmente com o que você está dizendo aqui.
user1118321

Respostas:

19

Pelo menos na minha opinião, a abstração prematura é bastante comum e foi especialmente tão cedo na história da OOP.

Pelo menos pelo que vi, o principal problema que surgiu foi que as pessoas leram os exemplos típicos de hierarquias orientadas a objetos. Eles foram instruídos a preparar tudo para lidar com as mudanças futuras que possam surgir (mesmo que não houvesse uma razão particularmente boa para acreditar que sim). Outro tema comum a muitos artigos durante algum tempo foram coisas como o ornitorrinco, que desafia regras simples sobre "os mamíferos são assim" ou "os pássaros são todos assim".

Como resultado, acabamos com um código que realmente precisava lidar com, digamos, registros de funcionários, mas que foram cuidadosamente escritos para estarem prontos, se você contratou um aracnídeo ou talvez um crustáceo.

Jerry Coffin
fonte
Portanto, a abstração prematura não é o ato de abstrair, é o ato de abstrair além do nível de abstração das especificações. Isso faz muito mais sentido, e eu pude ver isso realmente acontecendo. Acho que só preciso esclarecer aos meus colegas que estou trabalhando no nível de abstração descrito pelo meu gerente.
James
1
@ James: também pode significar abstrair "demais" as especificações da versão 1.0, pois você acredita que conhece um novo requisito nas especificações de uma versão posterior, portanto, já implementa a v1.0 de uma maneira mais geral do que a versão 1.0. necessário. Algumas vezes, é bom imaginar o que pode acontecer em uma versão posterior, mas também há um certo risco de excesso de engenharia.
Doc Brown
@DocBrown Eu chamaria isso de concreção prematura. O ato de abstração é remover informações, não adicioná-las. Se você estiver adicionando mais à abstração do que o necessário, estará assumindo algo sobre níveis mais baixos de abstração. Eu gostaria de diferenciar os dois, pois acho que chamar "abstração" não faz muito sentido, pois a definição de abstração é "o processo de extrair a essência subjacente" onde a concretização é "caracterizada por pertencer à experiência imediata ou de coisas ou eventos reais ".
James
6

É importante lembrar que a abstração é um meio para atingir um fim. Você usa a abstração para uniformizar o comportamento em seu programa e simplifica a adição de novas classes nos casos em que você espera que novas classes sejam adicionadas, mas somente quando a abstração é necessária naquele exato momento.

Você não adicionaria abstração simplesmente porque pode precisar (sem nenhuma base real para pensar que precisará adicionar novas classes no futuro). Uma metáfora apropriada aqui pode ser canalização. Espero que você concorde que um tubo de 6 direções que permita que a água flua para cima / baixo, leste / oeste, norte / sul seria o tipo mais flexível de tubo que você poderia ter. Teoricamente, você poderia usar um tubo de 6 direções em qualquer lugar em que ele fosse necessário e bloquear as direções desnecessárias, certo?

Se você tentasse resolver um problema com um vazamento embaixo da pia e descobrisse que todas as seções do tubo eram pedaços de tubos com 6 direções, você gostaria de arrancar o cabelo frustrado com o cara que o projetou dessa maneira. Não apenas você não sabe onde está o problema, mas seria quase certamente mais simples começar do zero de maneira adequada.

É claro que codificação não é canalização, mas a metáfora ainda permanece. Abstração é como usar aquelas peças de tubo de 6 direções. Use-os quando acreditar sinceramente que poderá um dia, no futuro próximo, conectar tubos de todas as 6 direções. Caso contrário, a abstração é simplesmente complicação, não muito diferente do que usar um padrão em que nenhum é necessário ou usar uma classe divina que tenta fazer tudo. Se não estiver sendo usado e provavelmente nunca será usado, você estará adicionando uma classe adicional por nada.

É certo que a arte de escrever programas é muito muito abstrata conceitualmente. Simplesmente vale a pena mencionar que as abstrações não existem por uma questão de abstração, mas porque são práticas de alguma maneira real. Se você sente necessidade de usar abstração, que seja, mas não me peça para verificar seu encanamento posteriormente. ;)

Neil
fonte
Você parece um pouco classcentrado ... Mesmo que existam muitas classes, a abstração não é limitada para ser usada / beneficiando-as.
Deduplicator
1
@Duplicator Fair o suficiente, mas isso não muda meu ponto de vista. Se a abstração for usada, ela deve ser imediatamente benéfica ou uma mudança planejada no futuro próximo. Não importa que estamos nos referindo a classes ou programação funcional.
911 Neil
3

Quando aprendi sobre Análise e Design Orientada a Objetos, há muitos anos, começamos com uma descrição clara em inglês do sistema que o cliente precisava.

Olhando para isso, qualquer substantivo (ou frase substantivo) seria considerado como uma classe possível. Quaisquer verbos (ou frases verbais) seriam métodos potenciais nas classes. Então "robô de ensacamento" pode ser uma aula BaggingRobot. "Abrir uma sacola" pode se tornar um método OpenBag.

Após algumas iterações, isso se tornaria um diagrama de classes.

Neste ponto, não há classes abstratas. O cliente não quer um conceito abstrato de robô de ensacamento. Eles querem um robô que coloque as coisas em sacos. Todas as classes são concretas e possuem um conjunto de métodos.

As classes abstratas são introduzidas apenas quando fica claro que:

  • Existem várias classes semelhantes que podem formar uma hierarquia.
  • Na verdade, eles compartilham algo em comum, para que uma classe base execute um propósito útil.

Para mim, "abstração prematura" pressupõe que alguém BaggingRobot deve herdar de alguns BaseRobote, pior ainda, tentar desenvolver um conjunto de métodos BaseRobotantes mesmo de você saber o que é comum a todos os robôs.

Simon B
fonte
Abstração não é sinônimo de classes abstratas. Uma interface é uma abstração. Estou falando de abstrações em geral, não apenas de classes abstratas. Talvez eu pudesse ter esclarecido isso usando uma interface. Se você desenvolver seu sistema na mesma camada de abstração do seu cliente, provavelmente será o caso de algumas classes abstratas que precisam ser estendidas, pois a linguagem é naturalmente abstrata. Trabalhar menos do que isso é o que chamo de "concretização prematura", em que você ingenuamente assume que as idéias dos clientes de como o sistema funcionará são iguais às suas.
James
Um objeto BaggingRobot que coloca objetos Item em objetos Bag já é uma abstração de um robô de empacotamento real que coloca coisas em sacos.
user253751
1

Parece que você quer resolver um problema que ainda não existe e que não tem boas razões para supor que isso ocorra, a menos que você acredite que o cliente está errado no que está solicitando. Se for esse o caso, eu recomendaria mais comunicação sobre a implementação de uma solução de suposição, abstrata ou não. Embora possa parecer inócuo dar um tapa na definição da classe e ser "seguro", sabendo que você pode estendê-la mais tarde, você pode achar que nunca precisa, ou pode se estender a ele de maneiras que complicam demais o design a linha, abstraindo as coisas erradas.

Seguindo o seu exemplo, você imagina que é o próprio robô , os itens que estão sendo ensacados ou o método de ensacamento que muda na linha?

Abstração prematura realmente significa apenas que você não tem um bom motivo para realizar a abstração e, como as abstrações estão longe de serem gratuitas em muitos idiomas, você pode estar incorrendo em sobrecarga sem justificativa.

Editar Para responder a alguns pontos do OP nos comentários, estou atualizando isso para esclarecer e responder de uma maneira que não exija uma longa cadeia de comentários ao abordar os pontos (1) e (2) mais especificamente.

(1) Para mim, isso parece que o programador não está sendo pragmático o suficiente, eles fizeram suposições de que as coisas existiriam no programa final que não funcionam , então eles estão trabalhando com um nível baixo de abstração, o problema não é abstração prematura, é concreção prematura.

(Ênfase minha). Presumir que existam coisas no programa final que não existe é exatamente o que vejo no seu exemplo. O cliente pediu um robô para empacotar itens. Esta é uma solicitação muito específica, se um pouco vaga em seu idioma. O cliente quer um robô que ensaca itens, para que você produza um par de objetos

public class Item {
/* Relevant item attributes as specified by customer */
}
public class BaggingRobot {
  private Collection<Item> items;

  public void bag(Item item);
}

Seu modelo é simples e preciso, segue os requisitos estabelecidos pelo cliente sem adicionar complexidade à solução e sem assumir que o cliente desejava mais do que solicitava. Se, quando apresentados, eles esclarecem a necessidade de o robô ter mecânica de ensacamento intercambiável, somente então você tem informações suficientes para justificar a criação de uma interface ou outro método de abstração, já que agora você pode apontar para um valor específico que é adicionado a o sistema através da abstração.

Pelo contrário, se você começa com a abstração a partir do pragmatismo puro, gasta tempo criando uma interface separada e implementando-a em uma concreção, ou cria uma classe abstrata ou qualquer forma de abstração que desejar. No caso de o cliente ficar satisfeito com isso, e nenhuma extensão adicional for necessária, você gastou tempo e recursos em vão e / ou introduziu sobrecarga e complexidade no sistema sem nenhum ganho.

No que diz respeito a (2), concordo que omitir coisas importantes não é, por si só, uma marca da abstração prematura. Abstração ou não, você pode omitir algo de importância e, a menos que seja encontrado abaixo da linha de uma cadeia de abstração, não será mais difícil ou mais fácil resolver isso de qualquer maneira.

Em vez disso , eu interpretaria isso como significando que qualquer abstração corre o risco de ofuscar seu modelo de domínio. A abstração incorreta pode dificultar a discussão de um sistema, pois você está criando relacionamentos que podem afetar drasticamente o crescimento adicional do modelo de domínio e a compreensão do domínio. Você corre o risco de omitir informações não importantes sobre a coisa que está sendo abstraída , mas sobre o sistema como um todo , colocando seu modelo na toca do coelho errada.

JimJam
fonte
"Parece que você quer resolver um problema que ainda não existe e que não tem boas razões para supor que isso ocorra, a menos que você acredite que o cliente está errado no que está solicitando". - Não foi nada disso que eu disse, na verdade deixei claro que estava construindo apenas com base nas palavras que o cliente usava. É o ato de não querer fazer isso o que me levou a usar abstrações.
James
"Eu recomendaria mais comunicação sobre a implementação de uma solução de palpite" - Você quer dizer como eu sugeri? Para citar o que eu disse no OP "a solução para isso não é apresentar sua própria concreção e desperdiçar recursos quando você descobrir que adivinhou errado, é obter mais informações do cliente".
James
"Embora possa parecer inócuo dar um tapa na definição de classe e se sentir seguro sabendo que você pode estendê-la mais tarde" - Quando digo "abstração", quero dizer "abstração", não quero dizer "palavra-chave abstrata". As interfaces podem ser abstrações, abstrações são lógicas nebulosas, é sobre isso que este post inteiro está falando. O cliente está se comunicando em termos de abstrações, portanto devemos programar em termos abstratos até sabermos mais sobre o domínio.
James
1
"você pode achar que nunca precisa" - Isso não faz nenhum sentido. Um cliente diz "Quero um robô que ensaca itens", você cria uma abstração que atende a esse critério. Quaisquer informações adicionais que você obtiver do cliente serão informações detalhadas sobre como eles desejam que a embalagem seja feita. Isso não invalida sua abstração, o cliente não muda repentinamente sobre uma ideia central por trás do que está comprando. O robô ainda vai guardar itens. A abstração que você criou ainda será aplicada.
James
"ou você pode se estender a estendê-lo de maneira a complicar demais o design, abstraindo as coisas erradas." ... Você leu honestamente o que eu disse ou apenas o título? A sério? Leia point (1) & (2)
James