quinta-feira, 1 de maio de 2014

Padrão de Projeto Flyweight

O padrão de projeto Flyweight, ou "peso-mosca", é um padrão estrutural que tem por finalidade usar de compartilhamento de objetos de granularidade fina, isto é, objetos que são iguais exceto por pequenos detalhes. Este padrão usa as informações comuns a diversos objetos em apenas um componente, o resto dos dados únicos são utilizados como parâmetros de métodos, reduzindo o número de objetos em uma aplicação, e assim agilizando o processamento da execução destes muitos objetos repetidos.
Para um bom resultado do uso deste padrão,use-o quando todas as afirmativas seguintes forem verdadeiras:

- A aplicação usa um grande número de objetos;
- Custos de armazenamento são grandes por causa da grande quantidade de objetos;
- A maior parte do estado dos objetos podem ser extrínseco (informações que dependem e variam com o contexto do flyweight, e que por esse motivo nao podem ser compartilhados);
- Muitos grupos de objetos podem ser substituídos por poucos objetos que possam ser compartilhados (estado extrínseco deve ser removido para outro lugar);
- A aplicação não depende da identidade do objeto;

Ainda deve-se pensar sobre 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 deles. Então ao utilizar o padrão deve ser analisado qual a prioridade no projeto, espaço ou tempo de execução.
Os participantes deste padrão seriam:

- Cliente : computa ou armazena o estado extrínseco do flyweight, assim como mantém a referência para ele;
- Flyweight: declara uma interface por onde pode receber e atuar sobre estados extrínsecos;
- FlyweightFactory: cria e gerencia objetos flyweights e garante que eles sejam compartilhados corretamente. Quando um cliente solicita um flyweight, um objeto FlyweightFactory fornece a ele, ou cria se caso ele não exista.
- ConcretesFlyweights (no exemplo do processador de texto seria o caractere em si): implementa a interface Flyweight e armazena só os estados intrísecos (compartilháveis).

Um bom exemplo da utilização é no desenvolvimento de jogos, afinal são usadas várias imagens que representam as entidades que compõe o jogo (cenário, jogador, inimigo..).
Isso causa duplicação de informação.Por exemplo, quando aparecem vários inimigos iguais no jogo, o mesmo conjunto de imagens é criado repeditas vezes.
Para exemplificar isso em código, começamos criando uma classe Imagem (informação intríseco):
  public class Imagem {
    protected String NomedaImagem;

    public Imagem(String imagem) {
        NomedaImagem = imagem;
    }

    public void desenharImagem() {
      System.out.println(NomedaImagem + " desenhada!");
    }
    }

Entao criar a classe Ponto (informação extrínseco):
  public class Ponto {
    public int x, y;

    public Ponto(int x, int y) {
        this.x = x;
        this.y = y;
    }
    }
Uma classe Flyweight será feita para armazenar o método de desenho da imagem em um determinado ponto:
 public abstract class SpriteFlyweight {
    public abstract void desenharImagem(Ponto ponto);
    }
Após a criação da classe Flyweight, criamos uma classe Flyweight Concreta, que seria a implementação da operação de fato:
 public class Sprite extends SpriteFlyweight {
    protected Imagem imagem;

    public Sprite(String nomeDaImagem) {
        imagem = new Imagem(nomeDaImagem);
    }

    @Override
    public void desenharImagem(Ponto ponto) {
        imagem.desenharImagem();
        System.out.println("No ponto (" + ponto.x + "," + ponto.y + ")!");
    }
    }
  
A classe fábrica guardará todas as imagens que serão compartilhadas e terá um método para pega-las, tendo assim todo acesso as imagens centralizado nessa classe:
 public class FlyweightFactory {

    protected ArrayList flyweights;

    public enum Sprites {
        JOGADOR, INIMIGO_1, INIMIGO_2, CENARIO_1
    }

    public FlyweightFactory() {
        flyweights = new ArrayList();
        flyweights.add(new Sprite("jogador.png"));
        flyweights.add(new Sprite("inimigo1.png"));
        flyweights.add(new Sprite("inimigo2.png"));
        flyweights.add(new Sprite("cenario1.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);
        default:
            return flyweights.get(3);
        }
    }
    }
 
E utilizando esse padrão:
 public static void main(String[] args) {
    FlyweightFactory factory = new FlyweightFactory();

    factory.getFlyweight(Sprites.JOGADOR).desenharImagem(new Ponto(10, 10));

    factory.getFlyweight(Sprites.INIMIGO_1).desenharImagem(
            new Ponto(100, 10));
    factory.getFlyweight(Sprites.INIMIGO_1).desenharImagem(
            new Ponto(120, 10));
    factory.getFlyweight(Sprites.INIMIGO_1).desenharImagem(
            new Ponto(140, 10));
    factory.getFlyweight(Sprites.INIMIGO_2).desenharImagem(
            new Ponto(60, 10));
    factory.getFlyweight(Sprites.INIMIGO_2).desenharImagem(
            new Ponto(50, 10));
    }
Pode ser implementado algo para eliminar um objeto nao usado mais, liberando ele da memória e diminuindo espaço.

Referências :
Livro - Padroes de Projetos - Solucoes Reutilizaveis - Gamma Erich
Site - http://brizeno.wordpress.com/category/padroes-de-projeto/flyweight/

Nenhum comentário:

Postar um comentário