C# - Padrões Estruturais Gof - Flyweight
Neste artigo vou apresentar o padrão estrutural Gof Flyweight. |
O padrão Flyweight é usado para criar muitos objetos pequenos relacionados sem invocar muito trabalho de sobrecarga ao fazer isso, melhorando assim o desempenho e a capacidade de manutenção.
Este padrão permite que os programas suportem grandes quantidades de objetos, mantendo um baixo o consumo de memória. Ele consegue isso compartilhando partes do estado do objeto entre vários objetos, ele economiza RAM armazenando em cache os mesmos dados usados por objetos diferentes.
Este padrão é
usado quando há a necessidade de criar um grande número de objetos parecidos, e
, como criar um grande número de objetos consome uma muita memória, o padrão
Flyweight fornece uma solução para minimizar o uso
de recursos reduzindo a carga na memória e compartilhando objetos.
Objetos Flyweights
Os objetos
compartilhados neste padrão são chamados Flyweights,
e, a chave para criarmos os objetos compartilhados é a distinção entre o estado
intrínseco e extrínseco de um objeto.
Assim cada objeto Flyweight possui duas partes :
Com base nisso este padrão permite que muitas instâncias de um objeto compartilhem seu estado intrínseco e, assim, reduzem o custo associado à sua criação.
Exemplo de aplicação do padrão Flyweight
Vamos ilustrar a
atuação do padrão com o seguinte exemplo:
Suponha que criamos e armazenamos um objeto Circulo
no cache (que estamos representando na figura abaixo)
O objeto Circulo armazenado no cache não tem cor.
Agora suponha que temos que criar 50.000 objetos Circulo com a cor
Amarela e 50.000 objetos Circulo na cor
Vermelha
e 50.000 objetos Circulo com a cor Azul :
Observe que todas as formas são as mesmas - temos somente objetos
Circulo - o que muda é apenas a cor.
Com certeza criar todos esses objetos vai consumir muita memória e isso é um
problema que temos que resolver. Assim este cenário pode ser replicado quando
você tem um grande numero de objetos que precisa criar.
O padrão Flyweight tenta melhorar o desempenho da seguinte forma:
- Ele cria o objeto círculo apenas uma vez e reutiliza esse objeto círculo várias vezes para criar um objeto com cor diferente;
Assim para obter os 50.000 objetos Circulo com a
cor Amarela ao invés de criar novos objetos
Circulo toda vez e preenche-los com a cor
amarela podemos obter o objeto circulo do cache e preencher esse objeto com
a cor amarela e podemos adotar o mesmo procedimento para criar os demais objetos
com a cor diferente.
Desta forma podemos melhorar o desempenho da aplicação pois estamos reduzindo a
criação dos objetos compartilhando
partes do estado do objeto.
Neste exemplo a
Forma do objeto, círculo, é constante (não sofre
alteração), assim ela refere-se ao estado intrínseco do objeto e
portanto, ela é armazenada na memória, ou seja, no cache.
A Cor não é constante e refere-se ao estado
extrínseco do objeto sendo calculada em tempo de execução e não é armazenada
na memória.
Diagrama UML
O diagrama UML do padrão Flyweight segundo o Gof apresenta os seguintes participantes
1- Flyweight : Interface que define os membros dos
objetos flyweight; Permite o compartilhamento, mas não o impõe. Os objetos
concretos que implementam esta interface podem ser compartilhados ou não
compartilhados;
2- ConcreteFlyweight : Implementa a interface Flyweight, adiciona armazenamento para o estado intrínseco. Deve ser compartilhável e qualquer estado que vamos armazenar neste objeto deve estar em um estado intrínseco.
3- UnsharedConcreteFlyweight : Implementa a interface Flyweight e adiciona armazenamento para a instância particular e não é compartilhada
4-FlyweightFactory
: Cria e gerencia objetos flyweight. Assegura que os objetos
flyweight são compartilhados de forma correta. Quando um cliente solicita um
flyweight, o objeto FlyweightFactory ativa uma instância existente ou cria uma,
se não houver nenhuma.
O uso de UnSharedFlyweight nem sempre é necessário,
depende dos requisitos.
Nota:
Embora este padrão utilize uma fabrica para criar a correta implementação da
interface Flyweight ele não deve ser confundido com o padrão Factory
Quando podemos usar o padrão Flyweight
Podemos usar este padrão :
- Quando muitos objetos semelhantes serão usados e o custo de armazenamento for
alto
- Quando você puder compartilhar estado entre objetos;
- Quando alguns objetos compartilhados facilmente substituiriam muitos objetos
não compartilhados
- Quando você quer economizar memória.
Vantagens do padrão Flyweight
Como vantagens deste padrão temos que :
- Reduz o uso de memória compartilhando objetos pesados.
- Favore o Cache de dados aprimorado para maior tempo de resposta.
- Aumenta o desempenho reduzindo o número de objetos pesados na memória
Desvantagem
Os objetos
Flyweights pode introduzir custos de tempo de execução associados à
transferência, localização e/ou computação do estado extrínseco, especialmente
se ele foi anteriormente armazenado como estado intrínseco.
Além disso, a aplicação do padrão possui um escopo de aplicação reduzido e de
acordo como Gof 5 condições devem ser consideradas para que o os benefícios do
padrão sejam tangíveis.
Aplicação prática do padrão Flyweight
A seguir veremos um exemplo pratico de aplicação do padrão Flyweight.
Suponha que
precisamos criar 50.000 objetos Circulos com
diferentes cores em uma aplicação. Assim teremos que criar :
10.000 objetos Círculos
- Amarelos
- Verdes
- Azuis
- Vermelhos
- Pretos
Cada objeto Circulo possui os seguintes atributos :
- Cor (não é constante)
- Coordenada X (fixo)
- Coordenada Y (fixo)
- Raio (fixo)
Com base nestas informações temos que :
- A Cor não é constante e representa o estado
extrínseco do objeto flyweight e vai ser atribuída em tempo de execução e
não será compartilhada;
- Forma, Coordenadas e o Raio são constantes e
representam os estados intrínsecos e serão armazenados no cache;
Levando em conta este cenário e estas considerações vamos implementar o padrão Flyweight usando uma aplicação Console .NET Core (.NET 5.0) criada no VS 2019 Community.
A seguir temos o diagrama de classes obtido a partir do VS 2019 na implementação do padrão:
A interface IForma representa o
Flyweight e possui o método Desenhar que define os
objetos flyweight;
A classe concreta Circulo representa o
ConcreteFlyweight e implementa a interface
IForma e adiciona armazenamento para o estado intrínseco. (x, y, raio,
forma) no cache;
A classe concreta FormaFactory representa a
FlyweightFactory e cria e compartilha objetos
flyweight;
A classe Program representa o
Client e usa a implementação do padrão com
referencias a FormaFactory;
A seguir temos o
código usado na implementação:
1- Interface IForma (Flyweight)
public interface IForma { void Desenhar(); } |
A interface IForma que define o contrato com a assinatura do método Desenhar que vai permitir criar os objetos flyweight que no nosso exemplo serão apenas círculos.
2- Classe Circulo (ConcreteFlyweight)
public class Circulo : IForma { //estado extrínseco public string Cor { get; set; } //estado intrínseco (cache) private int x = 10; private int y = 20; private int raio = 30; public void SetCor(string Cor) { this.Cor = Cor; } public void Desenhar() { Console.WriteLine($"Circulo: Desenhar() [Cor:{Cor} x:{x},y:{y}, raio:{raio}]"); } } |
A classe circulo implementa a interface e o método Desenhar para criar um objeto Circulo e define um método SetCor para poder atribuir o valor a propriedade Cor que representa o estado extrínseco e não será compartilhado. Esta classe também define os campos das coordenadas x, y e o raio que representam o estado intrínseco (ou seja são constantes) e serão armazenados no cache.
3- Classe FormaFactory (FlyweightFactory)
using System; using System.Collections.Generic;
namespace Flyweight2 // cria e gerencia objetos |
Usa um objeto
Dictionary(estático) como uma coleção de objetos
Forma para armazenar em cache os Círculos
criados.
Define o método GetForma (estático) que vai receber
a chave e vai verificar, com base na chave, se o objeto flyweight está no cache
ou não. Se estiver lá, ele retornará o objeto flyweight existente. E se
não estiver lá, ele criará um novo objeto flyweight e adicionará esse objeto ao
cache e retornará esse objeto flyweight.
4- Program
|
Neste código temos o seguinte :
- A classe
Program representa o
client e aqui vamos simular a criação de objetos
usando um laço for para criar apenas 3 objetos de cada cor
(apenas para vermos a implementação funcionando)
-
Chama o método GetForma passando a chave que é aqui é a string circulo
e realiza o Cast para o objeto Circulo
- Atribui a cor (estado extrinseco)
desenha o Circulo usando o método
Desenhar
-
Aqui iremos verificar se o objeto ja foi criado e esta no cache
se estiver retorna senao cria um novo objeto e adiciona ao cache
A execução do projeto irá apresentar o seguinte resultado:
Pegue o código do projeto aqui : Flyweight1.zip
"(Disse Jesus) Eu sou a videira verdadeira, e meu Pai é
o agricultor.
Todo ramo que, estando em mim, não der fruto, ele o corta; e todo o que dá
fruto limpa, para que produza mais fruto ainda."
João 15:1,2
Referências:
NET - Unit of Work - Padrão Unidade de ...
NET - O padrão de projeto Decorator
NET - Padrão de Projeto Builder
C# - O Padrão Strategy (revisitado)
NET - O padrão de projeto Command
NET - Apresentando o padrão Repository