Como programador, me encontro no dilema em que quero tornar meu programa o mais abstrato e geral possível.
Fazer isso normalmente me permitiria reutilizar meu código e ter uma solução mais geral para um problema que pode (ou não) surgir novamente.
Então essa voz na minha cabeça diz: apenas resolva o problema, manequim, é assim tão fácil! Por que gastar mais tempo do que você precisa?
Todos nós já enfrentamos essa questão em que a Abstração está no seu ombro direito e o Resolva-estúpido, no lado esquerdo.
Qual ouvir e com que frequência? Qual é a sua estratégia para isso? Você deveria abstrair tudo?
time-management
problem-solving
abstraction
Bryan Harrington
fonte
fonte
Respostas:
Nunca abstraia até que você precise.
Em Java, por exemplo, você deve usar interfaces. Eles são uma abstração.
No Python, você não possui interfaces, possui Duck Typing e não precisa de altos níveis de abstração. Então você simplesmente não.
Não abstraia até ter escrito três vezes.
Uma vez é - bem - uma vez. Basta resolvê-lo e seguir em frente.
Duas vezes é a indicação de que pode haver um padrão aqui. Ou não pode. Pode ser apenas coincidência.
Três vezes é o início de um padrão. Agora transcende a coincidência. Agora você pode abstrair com sucesso.
Não.
De fato, você nunca deve abstrair nada até ter uma prova absoluta de que está fazendo o tipo certo de abstração. Sem a "Regra das Três Repetições", você escreverá coisas que são inutilmente abstraídas de uma maneira que não ajuda.
Essa é uma suposição frequentemente falsa. Abstração pode não ajudar em nada. Isso pode ser feito mal. Portanto, não faça isso até que você precise.
fonte
Ah, YAGNI. O conceito mais abusado de programação.
Há uma diferença entre tornar seu código genérico e fazer um trabalho extra. Você deve gastar tempo extra para tornar seu código flexível e facilmente adaptado para fazer outras coisas? Absolutamente. Você deve gastar tempo implementando funcionalidades desnecessárias? Não. Você deve gastar tempo fazendo seu código funcionar com outro código que ainda não foi implementado? Não.
Como diz o ditado "Faça a coisa mais simples que poderia funcionar". O problema é que as pessoas sempre confundem simples com fácil . Simplicidade dá trabalho. Mas vale a pena o esforço.
Você pode ir ao mar? Claro. Mas minha experiência é que poucas empresas precisam mais de uma atitude "faça o que está fazendo agora" do que já tem. A maioria das empresas precisa de mais pessoas que pensem nas coisas antes do tempo. Caso contrário, eles sempre acabam tendo um problema auto-sustentável, onde ninguém nunca tem tempo para nada, todo mundo está sempre com pressa e ninguém faz nada.
Pense da seguinte maneira: é bem provável que seu código seja usado novamente de alguma forma. Mas você não vai prever corretamente dessa maneira. Portanto, torne seu código limpo, abstrato e com poucas conexões. Mas não tente fazer seu código funcionar com peças específicas de funcionalidade que ainda não existem. Mesmo se você souber que existirá no futuro.
fonte
simple
eeasy
!Um silogismo:
A generalidade é cara.
Você está gastando dinheiro de outras pessoas.
Portanto, a despesa de generalidade deve ser justificada para as partes interessadas.
Pergunte a si mesmo se você está realmente resolvendo o problema mais geral, a fim de economizar dinheiro às partes interessadas mais tarde, ou se acha apenas um desafio intelectual fazer uma solução geral desnecessariamente.
Se a generalidade for determinada como desejável, ela deverá ser projetada e testada como qualquer outro recurso . Se você não puder escrever testes que demonstrem como a generalidade implementada resolve um problema exigido pela especificação, não se preocupe em fazê-lo! Um recurso que não atende aos critérios de design e não pode ser testado é um recurso no qual ninguém pode confiar.
E, finalmente, não existe algo como "o mais geral possível". Suponha que você escreva software em C #, apenas por uma questão de argumento. Você fará com que todas as classes implementem uma interface? Toda classe uma classe base abstrata com todo método um método abstrato? Isso é bastante geral, mas não é nem de longe "o mais geral possível". Isso apenas permite que as pessoas alterem a implementação de qualquer método por meio de subclassificação. E se eles quiserem alterar a implementação de um método sem subclassificar? Você pode transformar todo método na verdade uma propriedade do tipo delegado, com um setter para que as pessoas possam mudar cada método para outra coisa. Mas e se alguém quiser adicionar mais métodos? Agora todos os objetos precisam ser expansíveis.
Nesse ponto, você deve abandonar o C # e adotar o JavaScript. Mas você ainda não chegou nem perto do geral; e se alguém quiser alterar como a pesquisa de membros funciona para esse objeto em particular? Talvez você devesse escrever tudo em Python.
Geralmente, aumentar a generalidade significa abandonar a previsibilidade e aumentar enormemente os custos de teste para garantir que a generalidade implementada tenha êxito em atender a uma necessidade real do usuário . Esses custos são justificados por seus benefícios para as partes interessadas que estão pagando por isso? Talvez eles sejam; depende de você discutir com as partes interessadas. Minhas partes interessadas não desejam que eu abandone a digitação estática em busca de um nível de generalidade totalmente desnecessário e extremamente caro, mas talvez o seu.
fonte
Só para esclarecer, fazer algo generalizado e implementar uma abstração são duas coisas completamente diferentes.
Por exemplo, considere uma função que copia memória.
A função é uma abstração que oculta como os 4 bytes são copiados.
int copy4Bytes (char * pSrc, char * pDest)
Uma generalização faria essa função copiar qualquer número de bytes.
int copyBytes (char * pSrc, char * pDest, int numBytesToCopy)
A abstração se presta a reutilizar, enquanto a generalização apenas torna a abstração útil em mais casos.
Mais especificamente relacionado à sua pergunta, a abstração não é apenas útil da perspectiva de reutilização de código. Muitas vezes, se feito corretamente, tornará seu código mais legível e fácil de manter. Usando o exemplo acima, o que é mais fácil de ler e entender se você está percorrendo o código copyBytes () ou um loop for iterando através de uma matriz que move dados um índice por vez? A abstração pode fornecer uma espécie de documentação própria que, na minha opinião, facilita o trabalho do código.
Como regra geral, se eu puder criar um bom nome de função que descreva exatamente o que pretendo que um pedaço de código faça, escrevo uma função para ele, independentemente de achar ou não que vou usá-lo novamente.
fonte
Uma boa regra geral para esse tipo de coisa é Zero, Um, Infinito. Ou seja, se você precisar do que está escrevendo mais de uma vez, pode assumir que precisará ainda mais vezes e generalizar. Esta regra implica que você não se incomoda com a abstração na primeira vez que escreve algo.
Outro bom motivo para essa regra é que, na primeira vez em que você escrever o código, você não saberá necessariamente o que abstrair, pois possui apenas um exemplo. A Lei de Murphy implica que, se você escrever um código abstrato pela primeira vez, o segundo exemplo terá diferenças que você não previu.
fonte
Eric Lippert aponta três coisas que eu acho que se aplicam em seu artigo à prova de um design . Eu acho que se você seguir, estará em boa forma.
fonte
Depende do motivo pelo qual você está codificando, o objetivo do seu projeto. Se o valor do seu código é que resolve um problema concreto, você deseja fazer isso e passar para o próximo problema. Se houver coisas rápidas e fáceis que você possa fazer para facilitar as coisas para futuros usuários do código (incluindo você mesmo), faça todos os meios possíveis.
Por outro lado, há casos em que o código que você está escrevendo serve a um propósito mais genérico. Por exemplo, quando você está escrevendo uma biblioteca para outros programadores, que a usarão em uma ampla variedade de aplicativos. Os usuários em potencial são desconhecidos e você não pode perguntar exatamente o que eles querem. Mas você deseja tornar sua biblioteca amplamente útil. Nesses casos, eu gastaria mais tempo tentando oferecer suporte à solução geral.
fonte
Sou um grande fã do princípio do KISS.
Eu me concentro em fornecer o que me pedem para fazer, e não na melhor solução. Eu tive que abandonar o perfeccionismo (e o TOC), porque isso me deixou infeliz.
fonte
Não concordo com "resolva estúpido" , acho que pode ser mais "resolva com inteligência" .
O que é mais inteligente:
A opção padrão deve ser a segunda. A menos que você possa demonstrar uma necessidade de geração.
A solução generalizada deve ser apenas quando você souber que é algo que será usado em vários projetos / casos diferentes.
Normalmente, acho que as soluções generalizadas são melhor servidas como "Código da Biblioteca Principal"
fonte