Mão na massa: Flyweight | Marcos Brizeno
Mão na massa: Flyweight
No desenvolvimento de jogos são utilizadas várias imagens. Elas representam as entidades que compõe o jogo, por exemplo, cenários, jogadores, inimigos, entre outros. Ao criar classes que representam estas entidades, é necessário vincular a elas um conjunto de imagens, que representam as animações.
Quem desenvolve jogos pode ter pensado na duplicação de informação quando as imagens são criadas pelos objetos que representam estas entidades, por exemplo, a classe que representa um inimigo carrega suas imagens. Quando são exibidos vários inimigos do mesmo tipo na tela, o mesmo conjunto de imagens é criado repetidamente.
A solução para esta situação de duplicação de informações pelos objetos é a utilização do padrão Flyweight.
"Usar compartilhamento para suportar eficientemente grandes quantidades de objetos de granularidade fina." [1]
Pela intenção percebemos que o padrão Flyweight cria uma estrutura de compartilhamento de objetos pequenos. Para o exemplo citado, o padrão será utilizado no compartilhamento de imagens entre as entidades.
Antes de exemplificar vamos entende um pouco sobre a estrutura do padrão. A classe Flyweight fornece uma interface com uma operação que deve ser realizado sobre um estado interno. No exemplo esta classe irá fornecer uma operação para desenhar a imagem em um determinado ponto. Desta forma a imagem é o estado intrínseco, que consiste de uma informação que não depende de um contexto externo. O ponto passado como parâmetro é o estado extrínseco, que varia de acordo com o contexto.
Vamos então ao código da imagem e do ponto:
protected String nomeDaImagem;
public Imagem(String imagem) {
nomeDaImagem = imagem;
System.out.println(nomeDaImagem + " desenhada!");
Para simplificar o exemplo, será apenas exibida uma mensagem no terminal, indicando que a imagem foi desenhada.
public Ponto(int x, int y) {
A classe Flyweight vai apenas fornecer a interface para desenho da imagem em um ponto.
public abstract class SpriteFlyweight {
public abstract void desenharImagem(Ponto ponto);
Outro componente da estrutura do Flyweight é a classe Flyweight concreta, que implementa a operação de faot:
public class Sprite extends SpriteFlyweight {
protected Imagem imagem;
public Sprite(String nomeDaImagem) {
imagem = new Imagem(nomeDaImagem);
public void desenharImagem(Ponto ponto) {
imagem.desenharImagem();
System.out.println("No ponto (" + ponto.x + "," + ponto.y + ")!");
Nesta classe também será apenas exibida uma mensagem no terminal para dizer que a imagem foi desenhada no ponto dado. O próximo componente da estrutura do Flyweight consiste em uma classe fábrica, que vai criar os vários objetos flyweight que serão compartilhados.
protected ArrayList<SpriteFlyweight> flyweights;
JOGADOR, INIMIGO_1, INIMIGO_2, INIMIGO_3, CENARIO_1, CENARIO_2
flyweights = new ArrayList<SpriteFlyweight>();
flyweights.add(new Sprite("jogador.png"));
flyweights.add(new Sprite("inimigo1.png"));
flyweights.add(new Sprite("inimigo2.png"));
flyweights.add(new Sprite("inimigo3.png"));
flyweights.add(new Sprite("cenario1.png"));
flyweights.add(new Sprite("cenario2.png"));
public SpriteFlyweight getFlyweight(Sprites jogador) {
switch (jogador) {
case JOGADOR:
return flyweights.get(0);
case INIMIGO_1:
return flyweights.get(1);
case INIMIGO_2:
return flyweights.get(2);
case INIMIGO_3:
return flyweights.get(3);
case CENARIO_1:
return flyweights.get(4);
return flyweights.get(5);
Além de criar os vários objetos a serem compartilhados, a classe fábrica oferece um método para obter o objeto, assim, o acesso a estes objetos fica centralizado e unificado a partir desta classe.
Para exemplificar a utilização do padrão, vejamos o seguinte código cliente:
factory.getFlyweight(Sprites.CENARIO_1).desenharImagem(new Ponto(0, 0));
factory.getFlyweight(Sprites.JOGADOR).desenharImagem(new Ponto(10, 10));
factory.getFlyweight(Sprites.INIMIGO_1).desenharImagem(
new Ponto(100, 10));
new Ponto(120, 10));
new Ponto(140, 10));
factory.getFlyweight(Sprites.INIMIGO_2).desenharImagem(
new Ponto(60, 10));
new Ponto(50, 10));
factory.getFlyweight(Sprites.INIMIGO_3).desenharImagem(
new Ponto(170, 10));
É exibido um conjunto de imagens para exemplificar o uso em um jogo. São desenhados inimigos de vários tipos, o cenário do jogo e o jogador. Note que o acesso aos objetos fica centralizado apenas na classe fábrica.
No desenvolvimento de jogos real, as referências dos objetos seriam espalhadas pelas entidades, garantindo a não duplicação de conteúdo.
O diagrama UML que representa esta implementação é o seguinte:
A solução implementada pelo padrão Flyweight é bem intuitiva. No entanto vale a pena comentar alguns detalhes. Percebeu que, na classe fábrica fica centralizado o acesso a todos os objetos compartilhados? O aconteceria se houvessem duas ou mais instâncias desta classe? Seriam criados vários objetos, sem nenhuma necessidade.
Para evitar este problema vale a pena dar uma olhada em outro padrão, o Singleton. Aplicando este padrão na classe fábrica, garantimos que apenas uma instância dela será utilizada em todo o projeto.
O ponto fraco do padrão é que, dependendo da quantidade e da organização dos objetos a serem compartilhados, pode haver um grande custo para procura dos objetos compartilhados. Então ao utilizar o padrão deve ser analisado qual a prioridade no projeto, espaço ou tempo de execução.
Imagine que existe um grupo de objetos que serão compartilhados juntos, por exemplo, uma sequência de objetos do cenário. Nesta situação, existe uma combinação com outro padrão, o Composite. Com ele é possível agrupar um conjuntos de objetos Flyweight que serão compartilhados juntos.
Outro ponto de interesse é a instanciação de todos os objetos flyweight na classe fábrica. Suponha que algum objeto é instanciado, mas nunca é utilizado? Pode ser implementado uma estratégia de garbage collection, que controla o número de instâncias de um determinado objeto. Ao não ser mais utilizado, o objeto é liberado da memória, reduzindo mais ainda o espaço.
Flyweight	Flyweight, Java, Padrões, Projeto	2 Comentários
← Mão na massa: Chain of Responsibility
Mão na massa: Facade →
2 comentários sobre "Mão na massa: Flyweight"
dezembro 18, 2015 às 5:40 PM
Muito bom o exemplo, dentro da realidade.
Só as linhas :
factory.getFlyweight(Sprites.CENARIO_1).desenharImagem(new Ponto(0, 0)); devem ser alteradas para factory.getFlyweight(FlyweightFactory.Sprites.CENARIO_1).desenharImagem(new Ponto(0, 0));
indicando a classe pertencente de Sprites.
novembro 14, 2017 às 8:40 AM
Ótimo texto! Só alguns erros de digitação:
– na parte "Outro componente da estrutura do Flyweight é a classe Flyweight concreta, que implementa a operação de faot", seria "de *fato*"
– e aqui onde está "Antes de exemplificar vamos entende um pouco sobre a estrutura do padrão. ", seria "[…] vamos *entender* […]"