Em algum código que estou revisando, estou vendo coisas com o equivalente moral do seguinte:
public class Foo
{
private Bar bar;
public MethodA()
{
bar = new Bar();
bar.A();
bar = null;
}
public MethodB()
{
bar = new Bar();
bar.B();
bar = null;
}
}
O campo bar
aqui é logicamente uma variável local, pois seu valor nunca se destina a persistir nas chamadas de método. No entanto, como muitos dos métodos Foo
precisam de um objeto do tipo Bar
, o autor do código original acabou de criar um campo do tipo Bar
.
- Isso é obviamente ruim, certo?
- Existe um nome para esse antipadrão?
object-oriented
terminology
anti-patterns
JSB ձոգչ
fonte
fonte
MethodA
ouMethodB
causaMethodA
ouMethodB
é chamado de alguma forma (ebar
é usado novamente, no caso debar.{A,B}()
o infrator), você tem problemas semelhantes, mesmo sem qualquer simultaneidade.Respostas:
Sim.
Isso torna os métodos não reentrantes, o que é um problema se eles forem chamados na mesma instância recursivamente ou em um contexto multithread.
Isso significa que o estado de uma chamada vaza para outra (se você esquecer de reinicializar).
Isso torna o código difícil de entender porque você precisa verificar o que foi dito acima para ter certeza do que o código está realmente fazendo. (Contraste com o uso de variáveis locais.)
Torna cada
Foo
instância maior do que precisa. (E imagine fazer isso para N variáveis ...)Na IMO, isso não merece ser chamado de antipadrão. É apenas um mau hábito de codificação / uso indevido de construções Java / código de porcaria. É o tipo de coisa que você pode ver no código de uma graduação quando o aluno está pulando aulas e / ou não tem aptidão para programar. Se você o vir no código de produção, é um sinal de que precisa fazer muito mais revisões de código ...
Para o registro, a maneira correta de escrever isso é:
Observe que também corrigi os nomes de métodos e variáveis para estar em conformidade com as regras de estilo aceitas para identificadores Java.
fonte
Eu chamaria isso de amplo escopo desnecessário para uma variável. Essa configuração também permite condições de corrida se vários threads acessarem o MethodA e o MethodB.
fonte
Este é um caso específico de um padrão geral conhecido como
global doorknobbing
. Ao construir uma casa, é tentador comprar apenas uma maçaneta e deixá-la por aí. Quando alguém quer usar uma porta ou armário, simplesmente pega a maçaneta da porta global. Se as maçanetas forem caras, pode ser um bom design.Infelizmente, quando seu amigo se aproxima e a maçaneta da porta é usada simultaneamente em dois lugares diferentes, a realidade se despedaça. Se a maçaneta da porta é tão cara que você pode pagar apenas uma, vale a pena criar um sistema que permita que as pessoas esperem sua vez pela maçaneta.
Nesse caso, a maçaneta da porta é apenas uma referência, por isso é barato. Se a maçaneta da porta era um objeto real (e eles estavam reutilizando o objeto em vez de apenas a referência), pode ser caro o suficiente para ser um
prudent global doorknob
. No entanto, quando é barato, é conhecido como acheapskate global doorknob
.Seu exemplo é da
cheapskate
variedade.fonte
A principal preocupação aqui seria a simultaneidade - se o Foo estiver sendo usado por vários threads, você terá um problema.
Além disso, é simplesmente bobagem - variáveis locais gerenciam seus próprios ciclos de vida perfeitamente. Substituí-los por variáveis de instância que precisam ser anuladas quando não são mais úteis é um convite para o erro.
fonte
É um caso específico de "escopo inadequado", com um lado de "reutilização variável".
fonte
Não é um anti-padrão. Os antipadrões têm alguma propriedade que faz parecer uma boa ideia, o que leva as pessoas a fazerem isso de propósito; eles são planejados como padrões e depois dá terrivelmente errado.
É também o que gera debates sobre se algo é um padrão, um antipadrão ou um padrão comumente mal aplicado que ainda tem usos em alguns lugares.
Isso está errado.
Para adicionar um pouco mais.
Esse código é supersticioso ou, na melhor das hipóteses, uma prática de culto à carga.
Uma superstição é algo feito sem uma justificativa clara. Pode estar relacionado a algo real, mas a conexão não é lógica.
Uma prática de culto à carga é aquela em que você tenta copiar algo que aprendeu de uma fonte mais experiente, mas na verdade está copiando os artefatos de superfície em vez do processo (chamado de um culto na Papua Nova Guiné que faria rádios de controle de aeronaves de bambu na esperança de fazer os aviões japoneses e americanos da Segunda Guerra Mundial voltarem).
Nos dois casos, não há nenhum argumento real a ser feito.
Um antipadrão é uma tentativa de uma melhoria razoável, seja na pequena (aquela ramificação extra para lidar com o caso extra que precisa ser tratado, que leva ao código de espaguete) ou na grande onde você implementa deliberadamente um padrão que seja desacreditado ou debatido (muitos descreveriam os singletons como tais, com alguns excluindo somente leitura - por exemplo, objetos de log ou somente leitura, por exemplo, objetos de definições de configuração - e alguns condenariam até mesmo esses) - ou então onde você está resolvendo o problema problema errado (quando o .NET foi lançado pela primeira vez, a MS recomendou um padrão para lidar com o descarte quando você tinha campos não gerenciados e campos gerenciados descartáveis - ele realmente lida com essa situação muito bem, mas o problema real é que você tem ambos os tipos de campo na mesma classe).
Como tal, um antipadrão é algo que uma pessoa inteligente que conhece bem o idioma, o domínio do problema e as bibliotecas disponíveis deliberadamente fará, que ainda tem (ou se argumenta ter) uma desvantagem que supera a vantagem.
Como nenhum de nós começa conhecendo bem um determinado idioma, domínio do problema e bibliotecas disponíveis, e como todo mundo pode perder alguma coisa ao passar de uma solução razoável para outra (por exemplo, comece a armazenar algo em um campo para um bom uso e tente refatorá-lo, mas não concluir o trabalho, e você terminará com o código como na pergunta) e, como todos nós perdemos as coisas de tempos em tempos no aprendizado, todos criamos algum código supersticioso ou de culto à carga em algum momento . A coisa boa é que eles são realmente mais claros de identificar e corrigir do que os antipadrões. Os verdadeiros anti-padrões não são, sem dúvida, anti-padrões, ou têm alguma qualidade atraente, ou pelo menos têm alguma maneira de atraí-los, mesmo quando identificados como ruins (muitas e poucas camadas são incontroversamente ruins, mas evitam uma) leva ao outro).
fonte
(1) Eu diria que não é bom.
Ele está realizando a manutenção manual, que a pilha pode fazer automaticamente com variáveis locais.
Não há benefício de desempenho, pois "novo" é chamado de cada invocação de método.
EDIT: O espaço de memória da classe é maior porque o ponteiro da barra está sempre ocupando memória durante a vida útil da classe. Se o ponteiro da barra fosse local, ele usaria apenas memória durante toda a vida útil da chamada do método. A memória do ponteiro é apenas uma gota no oceano, mas ainda é uma gota desnecessária.
A barra é visível para outros métodos da classe. Métodos que não usam bar não devem ser capazes de vê-lo.
Erros baseados em estado. Nesse caso em particular, os erros da base de estado só devem ocorrer no multiencadeamento, pois "new" é chamado a cada vez.
(2) Não sei se existe um nome para ele, pois na verdade existem vários problemas, não um.
- limpeza manual quando o automático está disponível
- estado desnecessário - estado
que vive mais tempo do que a vida inteira -
visibilidade ou escopo muito altos
fonte
Eu chamaria isso de "Xenofóbico Errante". Ele quer ser deixado em paz, mas acaba sem saber exatamente onde ou o que é. Portanto, é uma coisa ruim, como outros já declararam.
fonte
Indo na contramão aqui, mas embora eu não considere o exemplo dado um bom estilo de codificação, como está em sua forma atual, o padrão existe durante o teste de unidade.
Essa maneira bastante padrão de fazer testes de unidade
é apenas uma refatoração desse equivalente do código do OP
Eu nunca escreveria código, como é dado pelo exemplo do OP, ele grita refatoração, mas considero o padrão não inválido nem antipadrão .
fonte
bar
comonull
, o JUnit de qualquer maneira cria uma nova instância de teste para cada método de teste.Esse cheiro de código é chamado de Campo Temporário na Refatoração por Beck / Fowler.
http://sourcemaking.com/refactoring/temporary-field
Editado para adicionar mais explicações por solicitação dos moderadores: você pode ler mais sobre o cheiro do código no URL referenciado acima ou na cópia impressa do livro. Como o OP estava procurando o nome para esse cheiro antipadrão / código específico, pensei que seria útil citar uma referência até então não mencionada de uma fonte estabelecida com mais de 10 anos de idade, embora eu saiba perfeitamente que estou atrasado para o jogo sobre esta questão, por isso é improvável que a resposta seja votada ou aceita.
Se não for muito promocional, também faço referência e explico esse cheiro específico de código no meu próximo curso do Pluralsight, Fundamentos de refatoração, que espero que seja publicado em agosto de 2013.
Como forma de agregar mais valor, vamos falar sobre como corrigir esse problema específico. Existem várias opções. Se o campo for realmente usado apenas por um método, ele poderá ser rebaixado para uma variável local. No entanto, se vários métodos estiverem usando o campo para se comunicar (em vez de parâmetros), esse é o caso clássico descrito em Refatoração e poderá ser corrigido substituindo o campo por parâmetros ou se os métodos que funcionam com esse código estiverem próximos relacionada, Extract Class pode ser usada para extrair os métodos e os campos para sua própria classe, na qual o campo está sempre em um estado válido (talvez definido durante a construção) e representa o estado dessa nova classe. Se você seguir a rota do parâmetro, mas houver muitos valores para repassar, outra opção é introduzir um novo Objeto de parâmetro,
fonte
Eu diria que, quando você se dedica a isso, é realmente uma falta de coesão, e eu posso dar o nome de "Falso Coesão". Eu cunharia (ou, se estou obtendo de algum lugar e esquecendo, "emprestado") esse termo porque a classe parece ser coesa, pois seus métodos parecem operar em um campo de membro. No entanto, na realidade eles não, o que significa que a aparente coesão é realmente falsa.
Quanto a ser ruim ou não, eu diria claramente que é, e acho que Stephen C faz um excelente trabalho para explicar o porquê. No entanto, se ele merece ser chamado de "antipadrão" ou não, acho que ele e qualquer outro paradigma de codificação podem ter um rótulo, pois isso geralmente facilita a comunicação.
fonte
Além dos problemas já mencionados, o uso dessa abordagem em ambientes sem coleta automática de lixo (por exemplo, C ++) levaria a vazamentos de memória, pois os objetos temporários não são liberados após o uso. (Embora isso possa parecer um pouco exagerado, existem pessoas por aí que mudariam 'nulo' para 'NULL' e ficariam felizes com a compilação do código.)
fonte
Isso me parece uma péssima aplicação horrível do padrão Flyweight. Infelizmente, eu conheci alguns desenvolvedores que precisam usar a mesma "coisa" várias vezes em métodos diferentes e simplesmente colocá-la em um campo privado em uma tentativa equivocada de economizar memória ou melhorar o desempenho ou outra pseudo-otimização estranha. Em sua mente, eles estão tentando resolver um problema semelhante, pois o Flyweight visa solucionar apenas eles que estão fazendo isso em uma situação em que não é realmente um problema que precisa ser resolvido.
fonte
Eu já o encontrei muitas vezes, geralmente ao atualizar o código anteriormente escrito por alguém que escolheu o VBA For Dummies como seu primeiro título e passou a escrever um sistema relativamente grande em qualquer idioma em que se deparasse.
Dizer que é realmente péssima prática de programação é correto, mas pode ter sido apenas o resultado de não termos recursos revisados pela Internet e por pares, como todos os que desfrutamos hoje, que podem ter guiado o desenvolvedor original por um caminho "mais seguro".
No entanto, ele realmente não merece a tag "antipattern" - mas foi bom ver as sugestões de como o OP poderia ser recodificado.
fonte