AntiPadrão: Exército de Clones

Transcrição

AntiPadrão: Exército de Clones
Rodrigo dos Santo Lima n° Usp:4894732
AntiPadrão: Exército de Clones
Raízes do Problema:
Durante o desenvolvimento de um sistema criamos várias estruturas de dados diferentes que
facilitam nosso trabalho e aproximam o código da abstração desejada. Mas, em alguns casos, as
instâncias dessas estruturas são pontos num espaço discreto e finito, como posições em um tabuleiro
de xadrez, dias do mês, valorações para uma fórmula booleana, etc. Nesses caos, às vezes sem
perceber, o número de instâncias dessas estruturas no nosso programa chega ser muito superior ao
número possível de instâncias diferentes da mesma estrutura, ou seja, criamos vários objetos que
representam exatamente a mesma coisa e acabamos inundando a memória com clones.
Como se já não bastasse o problema da memória, durante a execução do programa é comum
existir alguma comparação entre duas instâncias dessa estrutura de dados e, para isso , criamos
métodos “compareTo” que comparam todos os pedacinhos das duas instâncias ao invés de
simplesmente ver se as duas referências apontam para o mesmo objeto. Além disso tudo também sabemos que é sempre “caro” criar objetos novos ,o que acaba nos
levando a um problema de eficiência.
Na verdade, o problema não surge somente quando lidamos com espaços discretos finitos,
muitas vezes criamos vários clones de coisas como Listas Ligadas, Grafos, Conjuntos, etc., sendo
que na maioria das vezes nem iremos modificá­los. Nesse caso criamos um problema maior ainda,
já que essas estruturas não são tão simples e provavelmente ocupam um espaço considerável na
memória.
Anecdotal Evidences:
1­ “Esses dois objetos do tipo Ponto não são iguais, mas representam o mesmo ponto...”
2­ “Ponto a = new Ponto(2,2);
:
Ponto b = new? Ponto(2,2);”
3­ “ ­ De agora em diante cada usuário terá seu espaço privado em disco onde deverá guardar
seus arquivos, fotos, músicas e etc...
­ Ótimo , só não esqueça de copiar para todos os usuários as Mp3s que todos ouvem.
­ Ah sim , claro, só acho que talvez vá demorar um pouco pois somos 20 usuários e cada
um gosta de pelo menos umas 600 músicas...”
Sintomas e Consequências:
1­ Seu simples programinha que busca uma solução ótima pra alguma coisa simples deu um
outOfMemoryError.
2­ Seu programa acha que duas coisas são diferentes mas pra você elas parecem idênticas.
3­ Há muitas chamadas para clone(), no seu programa.
4­ Seu programa fica lento fazendo comparações entre objetos.
Causas Conhecidas:
A principal causa é a falta de atenção do programador aos detalhes da sua implementação,
focando­se somente nos algoritmos e esquecendo que o programa é executado numa máquina com
limites. Esse tipo de problema é mais comum com programadores que só conhecem linguagens de
altíssimo nível e nunca tiveram que manipular diretamente os recursos da máquina.
Outra causa que está associada à inexperiência do programador é o medo da passagem de
referência como parâmetros nas chamadas de métodos. Com medo de ver seu objeto ser modificado,
o programador decide que deve passar um clone do objeto. O problema é que esse clone pode ficar
na memória por mais tempo do que se imagina e talvez nem ser alterado.
Exceções:
1­ Em situações onde sabemos que o número de instâncias de objetos idênticos é limitado
assim como o número de chamadas ao contrutor de objetos.
2­ Quando o número de objetos diferentes possíveis é ilimitado e há pouca chance de
existência de objetos iguais no sistema.
3­ Quando a redundancia é , de alguma forma essencial ao bom funcionamento do programa.
Solução Refatorada:
Podemos usar uma fábrica de objetos para resolver esse problema. A fábrica de objetos só
cria os objetos “inéditos” no sistema e mantém uma referência para cada objeto já criado. A fábrica
conhece todas as instâncias já criadas e fornece apenas referências para as mesmas. Para evitar as
clonagens dos objetos já existentes e manter a consistência das referências a esses objetos, devemos
garantir a imutabilidade desses objetos. Para isso temos que tomar cuidado para que os métodos que
alteram o objeto não o alterem mas retornem uma referência para o resultado da operação.
Exemplo:
Sabemos que os objetos do tipo ParOrdenado representam uma posição numa matriz
NxM.
No nosso código temos chamadas do tipo:
ParOrdenado parOrd = new ParOrdenado(x,y);
Dessa forma temos uma grande chance de, no decorrer da execução, criarmos vários pares ordenados que representam a mesma posição na matriz.
UML antes da refatoração:
Nesse caso, os métodos soma(),subtrai() e mudaXY() modificam o objeto.
Podemos refatorar da seguinte forma:
Cada vez que precisamos de um objeto ParOrdenado pedimos à fábrica dessa forma:
ParOrdenado parOrd =
FabricaDeParesOrdenados.pegaInstancia().parOrdenado(x,y);
Os métodos soma(), subtrai() e mudaXY() não alteram os ParesOrdenados envolvidos e retornam o ParOrdenado resultante. Ex:
public ParOrdenado soma(ParOrdenado outro){
FabricaDeParesOrdenados fabrica =
FabricaDeParesOrdenados.pegaInstancia();
return fabrica.parOrdenado(x + outro.getX(),y +
outro.getY());
}
Outra solução possível é redefinir o construtor da Classe (quando permitido pela linguagem)
e guardar uma coleção de todos os objetos diferentes dessa classe já criados numa variável estática,
ou numa Class Variable(em Smalltalk por exemplo).
Exemplos Conhecidos:
O Exército de Clones está presente na maioria dos Exercícios Programa que não conseguem
atingir o objetivo esperado por culpa de um OutOfMemoryError ou porque demoram tempo maior
do que o esperado para dar a resposta. Isso ocorre com frequência em Exercícios programa de
disciplinas como Algoritmos em Grafos, Inteligência Artificial,Estruturas De Dados, onde é comum
o uso de longas buscas que se expandem em muitos nós que muitas vezes possuem características
idênticas.
Outro exemplo não relacionado à programação é a presença de arquivos redundantes num
computador ou dados replicados num banco de dados (a não ser por propósitos como recuperação
de dados.).

Documentos relacionados