1. O Que é o Padrão Flyweight
1.1 Definição e Conceito
O Padrão Flyweight é um padrão de design estrutural cujo principal objetivo é minimizar o número de objetos compartilhados, poupando assim memória e melhorando o desempenho. O Padrão Flyweight reduz a criação e o consumo de objetos compartilhando objetos iguais ou semelhantes, alcançando otimização de desempenho.
1.2 Diferença de Outros Padrões de Design
Em comparação com outros padrões de design, o Padrão Flyweight concentra-se principalmente no compartilhamento e reutilização de objetos. Ele divide objetos em estados internos compartilháveis e estados externos não compartilháveis. Ao compartilhar estados internos, reduz a criação e o consumo de memória de objetos, melhorando a eficiência do sistema.
2. Características e Vantagens do Padrão Flyweight
As principais características e vantagens do Padrão Flyweight incluem:
- Uso minimizado de memória: Reduz o consumo de memória compartilhando objetos iguais ou semelhantes.
- Melhora de desempenho: Reduz a criação e destruição de objetos, acelerando a operação do sistema.
- Suporte para um grande número de objetos de granularidade fina: Pode criar um grande número de objetos de granularidade fina sem ocupar muito espaço de memória.
- Estrutura do sistema simplificada: Ao separar os estados internos e externos dos objetos, simplifica a estrutura e complexidade do sistema.
3. Exemplos de Aplicações Práticas do Padrão Flyweight
O Padrão Flyweight pode ser aplicado nos seguintes cenários:
- Objetos de partículas em jogos: As propriedades de cada objeto de partícula podem ser divididas em estados internos e externos, e objetos de partículas com propriedades iguais podem ser compartilhados.
- Objetos de conexão em servidores de rede: As propriedades dos objetos de conexão podem ser divididas em estados internos e externos, e objetos de conexão existentes podem ser reutilizados antes de serem reciclados.
4. Implementação do Padrão Flyweight em Golang
4.1 Diagrama de Classe UML
O diagrama de classe UML do Padrão Flyweight em Golang é o seguinte:
4.2 Introdução do Exemplo
Neste exemplo, iremos criar um editor gráfico baseado no Padrão Flyweight, contendo círculos de cores diferentes, e reduzir o uso de memória compartilhando objetos de círculo com a mesma cor.
4.3 Etapas de Implementação
4.3.1 Criar Interface Flyweight e Classe Concrete Flyweight
Primeiramente, precisamos criar uma interface Flyweight
para definir as operações de objetos compartilhados. Em seguida, podemos criar uma classe ConcreteFlyweight
para implementar a interface Flyweight
e incluir estados internos.
// Flyweight define a interface dos objetos flyweight
type Flyweight interface {
Operation(extrinsicState string)
}
// ConcreteFlyweight representa o objeto concreto flyweight, implementando a interface Flyweight
type ConcreteFlyweight struct {
intrinsicState string
}
// Operation implementa o método de operação do objeto compartilhado
func (f *ConcreteFlyweight) Operation(extrinsicState string) {
fmt.Printf("Objeto concreto flyweight, estado interno: %s, estado externo: %s\n", f.intrinsicState, extrinsicState)
}
4.3.2 Criar Classe FlyweightFactory
A seguir, podemos criar uma classe FlyweightFactory
para gerenciar e compartilhar objetos flyweight. Esta classe-fábrica mantém um dicionário flyweights
para armazenar os objetos flyweight criados.
// Classe FlyweightFactory
type FlyweightFactory struct {
flyweights map[string]Flyweight
}
// GetFlyweight recupera ou cria um objeto flyweight da fábrica
func (f *FlyweightFactory) GetFlyweight(key string) Flyweight {
if fw, ok := f.flyweights[key]; ok {
return fw
}
flyweight := &ConcreteFlyweight{
intrinsicState: key,
}
f.flyweights[key] = flyweight
return flyweight
}
4.3.3 Exemplo de Chamada pelo Cliente
Por fim, podemos criar uma classe Client
para demonstrar como usar o padrão flyweight para implementar um editor gráfico.
// Classe Cliente
type Cliente struct {
flyweight Flyweight
}
// Operation realiza uma operação
func (c *Cliente) Operation() {
c.flyweight.Operation("estado externo")
}
4.4 Considerações de Implementação e Melhores Práticas
4.4.1 Compartilhamento de Estado e Segurança de Thread
Ao usar o padrão flyweight, é necessário prestar atenção ao compartilhamento do estado interno e à segurança de threads. Como os objetos flyweight são compartilhados por vários clientes, é necessário garantir a consistência do estado interno.
4.4.2 Gerenciamento de Pool de Objetos
Para gerenciar e reutilizar melhor os objetos flyweight, podem ser usados pools de objetos para armazenar os objetos flyweight criados. Os pools de objetos podem aumentar a taxa de reutilização de objetos e reduzir o overhead da criação e destruição de objetos.
4.4.3 Gerenciamento Externo do Estado do Objeto
O padrão flyweight separa o estado interno e o estado externo dos objetos, mas o estado externo precisa ser gerenciado pelo cliente. Ao usar objetos flyweight, o cliente precisa passar o estado externo para o objeto flyweight para operação.
Exemplo de Código Completo
Abaixo está um exemplo de código completo em Golang:
package main
import "fmt"
// Flyweight define a interface de um objeto flyweight
type Flyweight interface {
Operation(estadoExterno string)
}
// ConcreteFlyweight representa um objeto flyweight específico e implementa a interface Flyweight
type ConcreteFlyweight struct {
estadoInterno string
}
// Operation implementa o método de operação para objetos compartilhados
func (f *ConcreteFlyweight) Operation(estadoExterno string) {
fmt.Printf("Objeto flyweight concreto, estado interno: %s, estado externo: %s\n", f.estadoInterno, estadoExterno)
}
// Classe FlyweightFactory
type FlyweightFactory struct {
flyweights map[string]Flyweight
}
// GetFlyweight recupera ou cria um objeto flyweight a partir da fábrica
func (f *FlyweightFactory) GetFlyweight(chave string) Flyweight {
if fw, ok := f.flyweights[chave]; ok {
return fw
}
flyweight := &ConcreteFlyweight{
estadoInterno: chave,
}
f.flyweights[chave] = flyweight
return flyweight
}
// Classe Cliente
type Cliente struct {
flyweight Flyweight
}
// Operation executa uma operação
func (c *Cliente) Operation() {
c.flyweight.Operation("estado externo")
}
func main() {
fabrica := &FlyweightFactory{
flyweights: make(map[string]Flyweight),
}
flyweight1 := fabrica.GetFlyweight("A")
flyweight1.Operation("estado externo 1")
flyweight2 := fabrica.GetFlyweight("B")
flyweight2.Operation("estado externo 2")
cliente := &Cliente{
flyweight: fabrica.GetFlyweight("A"),
}
cliente.Operation()
}