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.
Respostas:
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.
fonte
É 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. ;)
fonte
class
centrado ... Mesmo que existam muitas classes, a abstração não é limitada para ser usada / beneficiando-as.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étodoOpenBag
.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:
Para mim, "abstração prematura" pressupõe que alguém
BaggingRobot
deve herdar de algunsBaseRobot
e, pior ainda, tentar desenvolver um conjunto de métodosBaseRobot
antes mesmo de você saber o que é comum a todos os robôs.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. 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.
(Ê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
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.
fonte