Os piores anti-padrões que você já encontrou [fechado]


9

Quais são os piores anti-padrões que você encontrou em sua carreira como programador?

Estou envolvido principalmente com java, embora provavelmente seja independente da linguagem.

Eu acho que o pior é o que eu chamo de principal anti-padrão . Significa programa composto por uma classe extremamente grande (às vezes acompanhada de um par de turmas pequenas) que contém toda a lógica. Normalmente, com um grande loop no qual toda a lógica de negócios está contida, às vezes tendo dezenas de milhares de linhas de código.

Respostas:


38

Código comentado. Blocos, talvez centenas de linhas. Sendo a teoria, ei, é comentado, não faz mal algum e talvez precisemos dela no futuro.


7
Bem, pelo menos você pode excluí-lo com segurança. Diferente de alguém com quem trabalhei, que renomeia procedimentos e deixa o código morto acessível. Blech.
Jay

@Cyrena, você trabalhou com Eric também?!?
CaffGeek

Eu tinha um monte de código uma vez que usava #ifdef COMMENT para bloquear as coisas. Funcionou muito bem até que alguém definiu COMENTÁRIO.
Michael Kohne

9
Esse é outro caso em que o uso do controle de versão compensa. Você pode excluir o código sem precisar perguntar se pode ou não precisar dele no futuro, porque ele pode ser recuperado novamente com algumas teclas.
Jason B

5
Eu acho que o código comentado tem um lugar, se você o prefixar com um comentário que explique por que está lá. Às vezes, deixo um bloco comentado que representa um caminho em potencial que eu estava pensando em seguir e deixo com um comentário sobre o motivo de não ter feito. @ Jason, eu definitivamente ouvi-lo sobre este ser o papel do controle de versão, mas o código excluído no controle de versão não é muito detectável.
Nlawalker

19

Vou acertar outro óbvio com "copy-pasta". Copiar código quase idêntico ao que você deseja e alterar algumas coisas (em vez de extrair para um método).

Isso é especialmente predominante em alguns códigos de teste funcionais e de API do final dos anos 90: literalmente centenas (ou até milhares) de casos de teste quase idênticos que poderiam ter sido divididos em algumas funções com três ou quatro parâmetros - ou, melhor ainda , algo orientado a dados. Meu primeiro emprego fora da faculdade foi, literalmente, oito meses reescrevendo e refatorando milhares de linhas de massas de cópia que usavam métodos obsoletos. Quando terminei, os arquivos de teste tinham menos de um décimo do tamanho original e eram muito mais fáceis de manter (e legíveis!).


16

Penso que posso escrever muito sobre o Pattern Mania e soluções que poderiam ser resolvidas com muito menos esforço, mas prefiro apontar para um ótimo artigo que li recentemente com um exemplo fantástico de como uma solução simples pode ser complicada demais .

Como (não) escrever o fatorial em Java, colocamos uma fábrica no seu algoritmo


3
Impressionante! Agora, onde está o FactorialWebService? : P
FrustratedWithFormsDesigner

11
Eu chamo isso de febre padrão ... todo mundo entende isso em um ponto de sua carreira. O melhor entre nós cresce além disso. Eu tive uma entrevista em que o cara me perguntou quando eu deveria pensar em padrões. Eu disse a ele que os deixei evoluir para o sistema. Ele disse: "Não, você deve usar padrões desde o início".
Michael Brown

FactoryFactories são apenas um sintoma de querer adiar a escolha real, tanto quanto possível, mas ainda acaba codificando-o ou mapeando um valor de string externo para um pedaço de código. Injeção de dependência é uma solução melhor.

Os padrões são artefatos de bom design - eles devem ser tratados como tal. Eu gosto de descrevê-los como definições e não como soluções. Eles são úteis na comunicação, mas não necessariamente no design.
Michael K

Thanx por isso, boa leitura.
Syg


14

Regiões

Em C #, você pode definir uma região de código que pode ser recolhida no IDE, ocultando-a, a menos que você queira lidar com esse código. Eu participei de um projeto em que as regiões abrangem centenas de linhas (gostaria de estar exagerando) e havia várias regiões em uma função de mil linhas (novamente, eu gostaria de estar brincando).

No lado positivo, o desenvolvedor que criou a região fez um trabalho muito bom na identificação de funções específicas dentro de uma região. Tanto que eu era capaz de fazer um método de extração na região e seguir em frente.

As regiões incentivam os desenvolvedores a "esconder" sua porcaria à vista.


15
O +1 nas regiões incentiva os desenvolvedores a "esconder" sua porcaria à vista. No entanto, quando usado corretamente, acho que as regiões ajudam a agrupar logicamente o código e a encontrar facilmente mais tarde.
Darren Young

Este tem sido um problema com editores dobráveis. Eu costumava fazer o mesmo tipo de coisa quando programava pela última vez no OCCAM.
Michael Kohne

2
Eu amo regiões ... mas apenas para organização ... não escondendo resmas digitais de código.
IAbstract

3
+1. É também por isso que o uso de regiões viola a regra SA1124 do StyleCop DoNotUseRegions.
Arseni Mourzenko

11
Eu tenho projetos que trabalham com um grande número de campos em SQL, e o código que atualiza / cria é muitas linhas. Uma região a #region SQL Updatetorna dobrável, portanto é necessária menos rolagem.
JYelton


9

Métodos que não sobrecarregam:

// Do something
public int doSomething(Data d);

// Do same thing, but with some other data
public int doSomething2(SomeOtherData d);

Mostra claramente que o programador não entendeu a sobrecarga.



5

Codificação flexível , ou seja, quando os programadores se esforçam para evitar a codificação embutida e acabam do outro lado da escala - dependências "codificadas".


4

Mantenha o estado no cliente.

Uma vez trabalhado com um aplicativo da web em que todo o estado foi mantido no navegador. (Para garantir a escalabilidade sem problemas, todas as exceções foram ignoradas).


Você poderia por favor elaborar? Em um aplicativo da Web, tudo bem quando o único estado está no navegador (por exemplo, os cookies) e o servidor não compartilha nada. Ou você quer dizer 'estado' como no 'banco de dados de aplicativos'?
keppla

Estado: Como nos dados reais para operar.

OO, você tem minhas mais profundas simpatias.
keppla

4

Checkins maciços

Odeio quando vejo que um desenvolvedor não faz o check-in há mais de uma semana. Isso significa que ele está preso e não procurou ajuda, ou está reunindo vários recursos em um grande check-in. (Eu deixei de lado o pior cenário, ele simplesmente não está fazendo nada. Isso é fácil de resolver ... duas palavras parecem que você foi contratado.)

Ao fazer um grande check-in, você perde muitos benefícios do SCM, como poder vincular um conjunto de alterações a um recurso específico. Além disso, é provável que você tenha muita fusão a fazer e isso não é necessariamente fácil de acertar.


11
Não fazer nada nem sempre é o pior cenário. Às vezes é melhor ter que escrever a partir do zero do que ter código para reescrever ...
Malaquias

4

Atualmente, estou trabalhando com um pedaço de código legado e adoro a maneira como o codificador anterior obtém o primeiro elemento de uma lista:

String result;
for(int i = 0; i < someList.size(); i++) {
    result = someList.get(i);
    break;
}

Mas o pior que vi neste código é definir páginas JSPs em linha de classes, escrever todo o HTML, CSS e Javascript usando scriptlet e out.println :-(


4

Um dos piores anti-padrões comportamentais que eu vi foi uma loja que só permitia que o código fosse verificado no controle de versão depois que ele estava em produção. Juntamente com checkouts exclusivos no VSS, isso levou a um processo complicado de desfazer checkouts, se um defeito de produção precisasse ser corrigido antes do próximo lançamento, muito menos mais uma pessoa precisando alterar um determinado arquivo para o próximo lançamento.

O fato de ser uma política departamental tornou ainda pior do que o caso de um único desenvolvedor se comportando dessa maneira.


3

Bloqueando um literal String

synchronized("one") { /* block one A*/ }

synchronized("one") { /* block one B*/ }

Nomes de classe muito longos. (no JRE)

com.sun.java.swing.plaf.nimbus.
InternalFrameInternalFrameTitlePaneInternalFrameTitlePaneMaximizeButtonWindowNotFocusedState

Estrutura de herança insuficiente

com.sun.corba.se.internal.Interceptors.PIORB extends
com.sun.corba.se.internal.POA.POAORB extends
com.sun.corba.se.internal.iiop.ORB extends
com.sun.corba.se.impl.orb.ORBImpl extends
com.sun.corba.se.spi.orb.ORB extends
com.sun.corba.se.org.omg.CORBA.ORB extends 
org.omg.CORBA_2_3.ORB extends
org.omg.CORBA.ORB

Uma exceção que não é.

public interface FlavorException { }

Manipulação de erro inútil e enigmática

if (properties.size() > 10000)
   System.exit(0);

Criação desnecessária de objetos

Class clazz = new Integer(0).getClass();
int num = new Integer(text).intValue();

Lançando uma exceção para outros fins

try {
    Integer i = null;
    Integer j = i.intValue();
} catch (NullPointerException e) {
    System.out.println("Entering "+e.getStackTrace()[0]);
}

Usando objetos de instância para métodos estáticos.

Thread.currentThread().sleep(100);

Sincronizando em um campo não final

synchronized(list) {
   list = new ArrayList();
}

Cópia inútil de uma String constante

String s = new String("Hello world");

Chamada inútil para String.toString ()

String s = "Hello";
String t = s.toString() + " World";

Chama System.gc () para liberar memória

Definir variável local como null para liberar alguma memória

    // list is out of scope anyway.
    list = null;
}

Usando ++ i em vez de i ++ por razões de desempenho (ou qualquer outra micro-micro-otimização)


Estes não são necessariamente antipadrões, apenas códigos ruins.
Gary Willoughby

@ Gary, ou maus padrões de desenvolvimento que vejo repetidos. Talvez não esteja dentro da definição estrita de um antipadrão.
Peter Peterrey

11
@ Peter: "Chama o System.gc () para liberar memória", vi uma ocasião em que o .NET explodiria com a exceção OutOfMemory, a menos que o GC fosse chamado explicitamente. Claro que era mais uma exceção do que uma regra.
Coder

3

Código polvilhado com:

// TODO: 

ou

// TODO: implement feature 

sem informações adicionais.


2

Iterador para manequins:

Iterator iCollection = collection.iterator();
for(int i = 0 ; i < collection.size() ; i++ ){
    if(collection.get(i) == something){
       iCollection.remove();
    }
 }

Fábrica Singletono:

public class SomeObject{
   private SomeObject() {}
   public static SomeObject getInstance(){
       return new SomeObject();
   }
}

desenvolvimento orientado por if-else (também chamado de princípio de fechamento aberto - aberto para modificação próximo ao entendimento):

if (sth1){
...  
}else if(sth2){
..
}
...
..
else if(sth1000000000000){
...
}

StringPattern (também chamado StringObject):

a) sendercode
if(sth1)
   str+="#a";
if(sth2)
   str+="#b";
...
if(sth1000)
   str+="#n";

b) receiver
   regexp testing if str contains #a, #b, ... #n

No else ifentanto, muitas vezes é a única maneira de fazer as coisas. Estou pensando em cordas; não pode usar um interruptor. As condições devem ser iguais para que seja útil.
Michael K

IMHO cada if / else pode ser substituído por mapeamento simples ou padrão de visitante. Em caso de cordas ou seja, você pode ter arquivo de propriedades como: someString1 = some.package.ObjectToHandleSomeString1
Marcin Michalski

2
O singleton é uma fábrica . Não há nada de errado com esse código. A única coisa que pode estar errada é que o código externo faz a suposição de que toda chamada para getInstanceretorna a mesma instância. Tal suposição quebraria o encapsulamento e é de fato um dos anti-padrões mais comuns.
Aug2

exacly é por isso que é tão confuso :) se o método foi chamado create () ou ele iria retornar a mesma instância cada vez tudo seria perfeitamente bem
Marcin Michalski

2

Não é tanto um padrão de codificação, mas um padrão comportamental, é muito ruim: modificar algo no código (digamos que os requisitos mudaram) e depois ajustar todos os testes de unidade até que o código seja aprovado. Ajustando ou simplesmente removendo todo o código de teste do método de teste, mas deixando o método lá.

Isso está vinculado a um mais genérico, o padrão That'll Do , aqui está uma linha de código representativa para ele:

int num = new Integer( stringParam ).parseInt( stringParam );

Funciona, afinal.


2

Eu absolutamente desprezo a inversão da abstração ou a reinvenção de primitivas de baixo nível em cima de primitivas de alto nível. Às vezes, porém, isso é causado por designers de linguagem ruim, não por programadores ruins. Exemplos:

  1. Usando uma única classe de método sem variáveis ​​de membro e uma interface correspondente (implementada em termos de tabelas de ponteiros de função), em vez de um ponteiro de função. Observe que em linguagens como Java, você pode não ter escolha.

  2. Em MATLAB e R, a insistência de que tudo é um vetor / matriz e não um primitivo.

  3. Enquanto estamos atacando o MATLAB, que tal o fato de ele não ter números inteiros? Portanto, quando você precisa de um número inteiro, deve usar um duplo.

  4. Linguagens puramente funcionais, nas quais você precisa usar chamadas de função para escrever um loop.


1

Definitivamente seria copiar / colar, já vi muitos códigos ruins sendo feitos por causa disso, isso e codificação de cowboys e tudo o que deriva disso. (Classes de Deus, métodos extra grandes, algoritmos com más idéias, etc.)

E se os padrões de design forem permitidos, eu diria: Executando um projeto como um conjunto de ações de um plano, sem fazer nenhuma análise de design ou domínio.


1

Java multiuso -

Um java bean com muitas variáveis, usado em alguns tipos diferentes de operações. Cada operação usa algum subconjunto arbitrário das variáveis ​​do bean e ignora as outras. Algumas variáveis ​​para o estado da GUI, algumas variáveis ​​usadas para passar entre componentes, algumas que provavelmente nem são mais usadas. Os melhores exemplos não têm documentação, o que dificultaria a apreciação do padrão.

Além disso, não pode esquecer o amado

try{ 
   ... //many lines of likely dangerous operations
}
catch(Exception e){}

IMHO, exceções fedem. Eles são muito difíceis de acertar e acrescentam uma falsa sensação de segurança.
Coder

Qualquer que seja a sua opinião sobre o mau cheiro das exceções, engolir silenciosamente um deles deve cheirar mais.
Steve B.

1

Um ex-colega de trabalho tinha o hábito de reutilizar objetos substituindo suas propriedades, em vez de simplesmente criar novas. Eu nunca poderia imaginar a quantidade de problemas que isso causou ao implementar novos recursos.


1

Algo que me causou muita dor é o padrão "Grande mapa no céu". Jogando em torno de um mapa em vez de usar objetos adequados. Você não tem idéia de quais "variáveis" ele contém sem depuração e não sabe o que pode conter sem rastrear o código para trás. Normalmente mapeia Strings para objetos ou Strings para Strings, que você potencialmente deve analisar em primitivas.


Soa como confiar em Session e ViewState no ASP.NET para passar bocas de dados entre páginas, que, infelizmente, é muitas vezes necessária ...
Wayne Molina

1

Um dos meus antipadrões de desenvolvimento favoritos é o uso de um "design" de banco de dados que requer a adição contínua de colunas adicionais a uma ou mais tabelas programaticamente . Este é um primo do "design" que cria uma nova tabela para cada instância de uma entidade. Ambos atingem inevitavelmente as limitações do servidor de banco de dados, mas geralmente não até que o sistema esteja em produção há algum tempo.


0

Eu acho que um dos piores anti-padrões que eu já vi envolve o uso de uma tabela de banco de dados como armazenamento temporário em vez de usar a memória do computador.

O domínio do problema é proprietário, o que me impede de explicá-lo, mas não é necessário entender o problema básico. Este era um aplicativo GUI escrito em Java com um banco de dados back-end. Era para pegar certos dados de entrada, manipulá-los e depois confirmar os dados processados ​​no banco de dados.

Nosso projeto possui um algoritmo bastante complicado que salva valores intermediários para processamento posterior. Em vez de encapsular os objetos temporários em ... objetos, uma tabela de banco de dados foi criada como "t_object". Sempre que um valor foi calculado, ele foi adicionado a esta tabela. Depois que o algoritmo terminasse seu trabalho, ele selecionaria todos os valores intermediários e os processaria em um objeto Map grande. Após o término de todo o processamento, os valores restantes marcados para serem salvos serão adicionados ao esquema real do banco de dados e as entradas temporárias na tabela "t_object" serão descartadas.

A tabela também foi usada como uma lista exclusiva, os dados só poderiam existir uma vez. Isso poderia ter sido um recurso decente do design se tivéssemos implementado restrições na tabela, mas acabamos iterando por toda a tabela para ver se os dados existiam ou não. (Não, nem usamos consultas que usavam cláusulas where com CONTAINS)

Alguns dos problemas que encontramos devido a esse design foram especificamente depuração. O aplicativo foi criado para pipeline de dados, para que houvesse várias GUIs que processariam os dados antes de chegarem nesse algoritmo. O processo de depuração foi processar um caso de teste e pausar logo após concluir a seção acima. Em seguida, consultávamos o banco de dados para ver quais dados estavam contidos nesta tabela.

Outro problema que descobrimos foi que os dados não estavam sendo excluídos corretamente dessa tabela temporária, o que interferiria com as execuções no futuro. Descobrimos que isso se deve ao fato de as exceções não serem tratadas adequadamente e, portanto, o aplicativo não sair corretamente e não excluir os dados na tabela em que estava sob controle.

Se tivéssemos usado o Design Orientado a Objetos Básico e mantivemos tudo na Memória, esses problemas acima nunca teriam ocorrido. Primeiro, a depuração seria simples, pois poderíamos configurar facilmente pontos de interrupção no aplicativo e depois inspecionar a memória na pilha e na pilha. Em segundo lugar, após uma saída anormal do aplicativo, a memória java teria sido limpa naturalmente sem ter que se preocupar em excluí-la do banco de dados.

NOTA: Não estou dizendo que esse padrão é inerentemente ruim, mas neste exemplo eu achei desnecessário quando os princípios básicos de OO seriam suficientes.

Não tenho certeza de um nome para esse anti-padrão, pois é a primeira vez que vejo algo assim sendo feito. Quaisquer bons nomes que vocês possam pensar para esse padrão?


Eu não chamaria isso de um problema geral. Depende da quantidade de dados temporários que estão sendo armazenados e do que está sendo feito com eles.
GrandmasterB

Devo acrescentar mais ao post, mas o principal problema foi que, em vez de modificar objetos e acessar os dados da memória, os dados foram manipulados com o código sql hack. Isso levou a maior complexidade e problemas graves de desempenho.
jluzwick

2
Você não menciona o domínio do problema, mas nem sempre é uma coisa ruim, manter um estado em outro lugar como esse pode permitir a escalabilidade dos processos e a introspecção durante longos períodos de execução, também para ajudar na depuração. O acesso ao banco de dados estava causando problemas ou preocupações de desempenho?
Jé Queue

Editei o artigo para refletir melhor como isso foi usado em nosso projeto, mas tivemos vários problemas durante a depuração, especificamente porque tivemos que consultar o banco de dados para ver as variáveis ​​temporárias. Também tivemos grandes problemas relacionados ao desempenho, devido ao fato de o banco de dados ser constantemente consultado por dados e verificar se novos dados já existiam.
Jluzwick #

0

Carrinho antes do cavalo - aka YouMightNeedIt

Por exemplo:

  • Criando um esquema de RDMBs com conceitos abstratos, referências cruzadas - em essência um cubo de dados super generalizado ... E ENTÃO escrevendo recursos em torno de um modelo de tudo faz.

Esse seria o irmão gêmeo mau de YAGNI, certo?
Wayne Molina

0

Na IMO, o pior antipadrão que já vi é o "Não precisamos de padrões fedorentos". Antipadrão: a ideia de que os padrões de design são desperdícios de tempo e você pode escrever código mais rapidamente, basta juntá-lo e copiar / colando conforme necessário.

Menção honrosa diz respeito ao código que carrega um objeto do banco de dados usando o antigo estilo VB6 de:

Foobar oFoo = new Foobar();
oFoo.FooID = 42;
if (oFoo.Load()) { 
    // do something with oFoo
}

não é realmente um antipadrão, por si só, mas mostra uma falta de tirar proveito da arquitetura adequada e da separação de preocupações.

Além disso, coisas assim:

// this name is misleading, we may not always want to stand in fire,
// we may want to stand in slime or voidzones or ice patches...
public Foobar StandInFire() { }

// why is this here???
public string BeatWithNerfBat(string whom) { }

// ????
public int GivePony(string to) { }
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.