8. Martelo de ouro
Um martelo de ouro é um antipadrão definido pela confiança de que uma solução específica é universalmente aplicável. Exemplos:-
Depois de encontrar um problema e encontrar um padrão para a solução perfeita, um programador tenta colocar esse padrão em todos os lugares, aplicando-o a projetos atuais e futuros, em vez de procurar soluções adequadas para casos específicos.
-
Alguns desenvolvedores já criaram sua própria variante de cache para uma situação específica (porque nada mais era adequado). Mais tarde, no próximo projeto que não envolvia nenhuma lógica de cache especial, eles usaram sua variante novamente em vez de usar bibliotecas prontas (por exemplo, Ehcache). O resultado foi um monte de bugs e incompatibilidades, além de muito tempo perdido e nervos à flor da pele.
Qualquer um pode cair nesse antipadrão. Se você for iniciante, talvez não tenha conhecimento sobre padrões de projeto. Isso pode levá-lo a tentar resolver todos os problemas da maneira que você domina. Se estamos falando de profissionais, chamamos isso de deformação profissional ou visão nerd. Você tem seus próprios padrões de design preferidos e, em vez de usar o certo, usa o seu favorito, assumindo que um bom ajuste no passado garante o mesmo resultado no futuro.
Essa armadilha pode produzir resultados muito tristes — desde uma implementação ruim, instável e difícil de manter até o fracasso total do projeto. Assim como não existe uma pílula para todas as doenças, também não existe um padrão de design para todas as ocasiões.
9. Otimização prematura
A otimização prematura é um antipadrão cujo nome fala por si.10. Código do espaguete
O código espaguete é um antipadrão definido por um código mal estruturado, confuso e difícil de entender, contendo todos os tipos de ramificação, como exceções de agrupamento, condições e loops. Antes, o operador goto era o principal aliado desse antipadrão. As instruções Goto não são mais usadas, o que felizmente elimina uma série de dificuldades e problemas associados.public boolean someDifficultMethod(List<String> XMLAttrList) {
...
int prefix = stringPool.getPrefixForQName(elementType);
int elementURI;
try {
if (prefix == -1) {
...
if (elementURI != -1) {
stringPool.setURIForQName(...);
}
} else {
...
if (elementURI == -1) {
...
}
}
} catch (Exception e) {
return false;
}
if (attrIndex != -1) {
int index = attrList.getFirstAttr(attrIndex);
while (index != -1) {
int attName = attrList.getAttrName(index);
if (!stringPool.equalNames(...)){
...
if (attPrefix != namespacesPrefix) {
if (attPrefix == -1) {
...
} else {
if (uri == -1) {
...
}
stringPool.setURIForQName(attName, uri);
...
}
if (elementDepth >= 0) {
...
}
elementDepth++;
if (elementDepth == fElementTypeStack.length) {
...
}
...
return contentSpecType == fCHILDRENSymbol;
}
}
}
}
}
Parece horrível, não é? Infelizmente, este é o antipadrão mais comum :( Mesmo a pessoa que escreve esse código não será capaz de entendê-lo no futuro. Outros desenvolvedores que virem o código pensarão: "Bem, se funcionar, tudo bem - é melhor não tocá-lo". Muitas vezes, um método é inicialmente simples e muito transparente, mas à medida que novos requisitos são adicionados, o método é gradualmente sobrecarregado com mais e mais declarações condicionais, tornando-o uma monstruosidade como esta. Se tal método aparecer, você precisa refatorá-lo completamente ou pelo menos as partes mais confusas. Normalmente, ao agendar um projeto, o tempo é alocado para refatoração, por exemplo, 30% do tempo do sprint é para refatoração e testes. Claro, isso pressupõe que não há pressa (mas quando isso acontece).aqui .
11. Números mágicos
Os números mágicos são um antipadrão no qual todos os tipos de constantes são usados em um programa sem nenhuma explicação de seu propósito ou significado. Ou seja, geralmente são mal nomeados ou, em casos extremos, não há nenhum comentário explicando o que são os comentários ou por quê. Assim como o código espaguete, esse é um dos antipadrões mais comuns. Alguém que não escreveu o código pode ou não ter a menor ideia sobre os números mágicos ou como eles funcionam (e com o tempo, o próprio autor não será capaz de explicá-los). Como resultado, alterar ou remover um número faz com que o código pare de funcionar magicamente. Por exemplo, 36 e 73. Para combater esse antipadrão, recomendo uma revisão de código. Seu código precisa ser examinado por desenvolvedores que não estão envolvidos nas seções relevantes do código. Seus olhos estarão frescos e eles terão perguntas: o que é isso e por que você fez isso? E claro, você precisa usar nomes explicativos ou deixar comentários.12. Programação de copiar e colar
A programação de copiar e colar é um antipadrão no qual o código de outra pessoa é copiado e colado sem pensar, possivelmente resultando em efeitos colaterais inesperados. Por exemplo, copiar e colar métodos com cálculos matemáticos ou algoritmos complexos que não entendemos completamente. Pode funcionar para o nosso caso particular, mas em algumas outras circunstâncias pode causar problemas. Suponha que eu precise de um método para determinar o número máximo em uma matriz. Vasculhando a Internet, encontrei esta solução:public static int max(int[] array) {
int max = 0;
for(int i = 0; i < array.length; i++) {
if (Math.abs(array[i]) > max){
max = array[i];
}
}
return max;
}
Obtemos um array com os números 3, 6, 1, 4 e 2, e o método retorna 6. Ótimo, vamos mantê-lo! Mais tarde, porém, obtemos uma matriz que consiste em 2,5, -7, 2 e 3 e, em seguida, nosso resultado é -7. E esse resultado não é bom. O problema aqui é que Math.abs() retorna o valor absoluto. Ignorar isso leva ao desastre, mas apenas em certas situações. Sem uma compreensão profunda da solução, há muitos casos que você não poderá verificar. O código copiado também pode ir além da estrutura interna do aplicativo, tanto no estilo quanto em um nível arquitetônico mais fundamental. Esse código será mais difícil de ler e manter. E, claro, não devemos esquecer que copiar o código de outra pessoa é um tipo especial de plágio.
13. Reinventando a roda
Reinventar a roda é um antipadrão, também conhecido como reinventar a roda quadrada. Em essência, esse modelo é o oposto do antipadrão copiar e colar considerado acima. Nesse antipadrão, o desenvolvedor implementa sua própria solução para um problema para o qual já existem soluções. Às vezes, essas soluções existentes são melhores do que as inventadas pelo programador. Na maioria das vezes, isso leva apenas a perda de tempo e menor produtividade: o programador pode não encontrar uma solução ou pode encontrar uma solução que está longe de ser a melhor. Dito isso, não podemos descartar a possibilidade de criar uma solução independente, porque fazer isso é um caminho direto para a programação de copiar e colar. O programador deve guiar-se pelas tarefas específicas de programação que se apresentam de forma a resolvê-las com competência, quer recorrendo a soluções prontas quer a criar soluções à medida. Muitas vezes, a razão para usar esse antipadrão é simplesmente a pressa. O resultado é uma análise superficial de (busca por) soluções prontas. Reinventar a roda quadrada é um caso em que o antipadrão em consideração tem um resultado negativo. Ou seja, o projeto exige uma solução personalizada e o desenvolvedor a cria, mas mal. Ao mesmo tempo, uma boa opção já existe e outras estão usando com sucesso. Resumindo: uma grande quantidade de tempo é perdida. Primeiro, criamos algo que não funciona. Em seguida, tentamos refatorá-lo e, finalmente, substituímos por algo que já existia. Um exemplo é implementar seu próprio cache personalizado quando já existem várias implementações. Não importa o quão talentoso você seja como programador, lembre-se de que reinventar uma roda quadrada é, no mínimo, uma perda de tempo. E, como você sabe, o tempo é o recurso mais valioso.14. Problema de ioiô
O problema ioiô é um antipadrão no qual a estrutura do aplicativo é excessivamente complicada devido à fragmentação excessiva (por exemplo, uma cadeia de herança excessivamente subdividida). O "problema ioiô" surge quando você precisa entender um programa cuja hierarquia de herança é longa e complexa, criando chamadas de método profundamente aninhadas. Como resultado, os programadores precisam navegar entre muitas classes e métodos diferentes para inspecionar o comportamento do programa. O nome desse antipadrão vem do nome do brinquedo. Como exemplo, vejamos a seguinte cadeia de herança: Temos uma interface de Tecnologia:public interface Technology {
void turnOn();
}
A interface Transport a herda:
public interface Transport extends Technology {
boolean fillUp();
}
E então temos outra interface, GroundTransport:
public interface GroundTransportation extends Transport {
void startMove();
void brake();
}
E a partir daí, derivamos uma classe abstrata Car:
public abstract class Car implements GroundTransportation {
@Override
public boolean fillUp() {
/* some implementation */
return true;
}
@Override
public void turnOn() {
/* some implementation */
}
public boolean openTheDoor() {
/* some implementation */
return true;
}
public abstract void fixCar();
}
A seguir, a classe abstrata do Volkswagen:
public abstract class Volkswagen extends Car {
@Override
public void startMove() {
/* some implementation */
}
@Override
public void brake() {
/* some implementation */
}
}
E, finalmente, um modelo específico:
public class VolkswagenAmarok extends Volkswagen {
@Override
public void fixCar(){
/* some implementation */
}
}
Essa cadeia nos obriga a buscar respostas para perguntas como:
-
Quantos métodos
VolkswagenAmarok
tem? -
Que tipo deve ser inserido no lugar do ponto de interrogação para obter a máxima abstração:
? someObj = new VolkswagenAmarok(); someObj.brake();
15. Complexidade acidental
A complexidade desnecessária é um antipadrão no qual complicações desnecessárias são introduzidas em uma solução.public void createDescriptionForElement(ServiceType type, Long languageId, Long serviceId, String description)throws Exception {
switch (type){
case CAR:
jdbcTemplate.update(CREATE_RELATION_WITH_CAR, languageId, serviceId, description);
case USER:
jdbcTemplate.update(CREATE_RELATION_WITH_USER, languageId, serviceId, description);
case FILE:
jdbcTemplate.update(CREATE_RELATION_WITH_FILE, languageId, serviceId, description);
case PLAN:
jdbcTemplate.update(CREATE_RELATION_WITH_PLAN, languageId, serviceId, description);
case CUSTOMER:
jdbcTemplate.update(CREATE_RELATION_WITH_CUSTOMER, languageId, serviceId, description);
default:
throw new Exception();
}
}
E, consequentemente, temos esta enumeração:
public enum ServiceType {
CAR(),
USER(),
FILE(),
PLAN(),
CUSTOMER()
}
Tudo parece ser simples e bom... Mas e os outros métodos? Na verdade, todos eles também terão um monte de switch
instruções e um monte de consultas de banco de dados quase idênticas, o que, por sua vez, complicará e sobrecarregará muito nossa classe. Como tudo isso poderia ser facilitado? Vamos atualizar um pouco nosso enum:
@Getter
@AllArgsConstructor
public enum ServiceType {
CAR("cars_descriptions", "car_id"),
USER("users_descriptions", "user_id"),
FILE("files_descriptions", "file_id"),
PLAN("plans_descriptions", "plan_id"),
CUSTOMER("customers_descriptions", "customer_id");
private String tableName;
private String columnName;
}
Agora cada tipo tem os nomes dos campos originais de sua tabela. Como resultado, o método para criar uma descrição torna-se:
private static final String CREATE_RELATION_WITH_SERVICE = "INSERT INTO %s(language_id, %s, description) VALUES (?, ?, ?)";
public void createDescriptionForElement(ServiceType type, Long languageId, Long serviceId, String description) {
jdbcTemplate.update(String.format(CREATE_RELATION_WITH_SERVICE, type.getTableName(), type.getColumnName()), languageId, serviceId, description);
}
Conveniente, simples e compacto, você não acha? A indicação de um bom desenvolvedor não é nem a frequência com que ele usa padrões, mas sim a frequência com que ele evita os antipadrões. A ignorância é o pior inimigo, porque você precisa conhecer seus inimigos de vista. Bem, isso é tudo que tenho para hoje. Obrigado a todos! :)