4 de dezembro de 2009

Design Patterns com Delphi : FlyWeight - parte 1

Mesmo com o barateamento dos chips de memória e o consequente aumento da disponibilidade desse recurso nos computadores mais modernos é uma boa ideia que, na construção de programas, se use com inteligência esse recurso. Programas que usam menos memória são mais leves para o Sistema Operacional gerenciar e acabam sendo mais responsivos ao usuário, isto é, atendem mais prontamente qualquer ação requisitada pelo usuário.

Imagine que você está criando um editor de textos que permite a formatação de trechos com fontes com características diferentes - negrito ou uma cor, por exemplo - em qualquer ponto do texto. O primeiro impulso é o de criar uma classe que tenha como membros o pedaço de texto em questão e a formatação aplicada - inclusive uma instância da Fonte. Num texto muito longo e ricamente formatado, pode haver centenas de ocorrências. Então, o consumo de memória com a solução apresentada pode tornar a navegação no texto bastante lenta, prejudicando a experiência do usuário.

Por isso, racionalizar a criação de instâncias de Fontes é uma boa saída. Para cenários como o descrito acima, foi criado o Design Pattern estrutural FlyWeight - ou Peso Mosca, numa referência à categoria de lutadores de boxe mais leves. Nesse pattern, a racionalização do uso da memória se dá através do compartilhamento das partes imutáveis das instâncias das classes. No caso dos fontes, isso significa criar uma parte fixa com a representação gráfica de cada caractere (e outras informações relevantes para desenhá-los) enquanto as variações de formatação permitidas, como negrito, itálico e a cor da letra, podem ser armazenadas da forma tradicional, dentro da própria instância da classe aonde a formatação está aplicada.

O conjunto dos valores imutáveis que compõem a instância da Fonte é chamado de Propriedades Intrínsicas, porque são elas que definem quem a Fonte é, independente do contexto onde ela será usada. É esse conjunto que será compartilhado para poupar o uso de memória. Em contrapartida, os valores que poderão variar de acordo com o contexto são denominados Propriedades Extrínsicas. Elas são livres para assumir qualquer valor que seja necessário para o contexto em que são usadas e, por isso, não podem ser compartilhadas, sendo que cada classe que fará uso delas deverá ter sua própria cópia.

Segue um esquema UML representando as classes envolvidas no pattern FlyWeight e suas associações:
Diagrama UML para FlyWeight

Conforme representado no diagrama acima, a nomenclatura para as classes envolvidas na implementação do FlyWeight é a seguinte :
FlyWeight contem as propriedades intrínsecas comuns do objeto, isto é, todos os dados que serão compartilhados. Ela também declara como interfaces as operações que poderão ser aplicadas com dados extrínsecos, dados esses que são passados como parâmetros na operação. A operação, então, usa as informações fornecidas como parâmetro para obter o resultado desejado, preservando no entanto o estado original do objeto. No exemplo, esse papel é exercido pela classe TWFonte, que introduz todas as informações que permitem desenhar os caracteres no padrão do fonte. A operação Desenha publicada pela classe aceita como parâmetros o local para desenho (contexto) e características temporárias que configuram cada operação de desenho. Por exemplo, esta operação pode desenhar um texto em negrito e vermelho num momento e em outro instante desenhar em itálico e verde - tudo calculado localmente com base nos dados globais de desenho e nas configurações estipuladas pelos parâmetros.
Os ConcreteFlyWeight são classes concretas que implementam as operações exigidas pela interface FlyWeight. Eles podem também introduzir novos dados intrínsecos, isto é, que serão usados pelas operações mas que são preservados - e, consequentemente, compartilhados por todas as instâncias. No exemplo, as classes TWArial e TWNewTimesRoman exercem esse papel.
O FlyweightFactory é uma espécie de Factory Method que mantem uma lista com as instâncias compartilhadas dos FlyWeights. A ideia é que sempre que for necessário obter uma instância de classe FlyWeight, aciona-se o Factory. Se uma instância já existir, ela é retornada; caso contrário, o Factory cria uma nova instância e adiciona-a à sua lista interna. Neste cenário, o Factory é reponsável por liberar a memória consumida pelas instâncias criadas por ele, quando for apropriado. No diagrama mostrado acima, a classe TWFontFactory desempenha essa tarefa.
O Client é toda classe que faz uso das operações publicadas pelo FlyWeight. Ela deve armazenar os dados extrínsecos para poder informá-los quando tiver que comandar uma das operações. Ela também pode manter referências à instâncias das classes FlyWeight. A classe TWTextoFormatado representada no diagrama é um exemplo desse papel.



Nenhum comentário :

Postar um comentário

OBS: Os comentários enviados a este Blog são submetidos a moderação. Por isso, eles serão publicados somente após aprovação.

Observação: somente um membro deste blog pode postar um comentário.