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:

Padrão Flyweight em Golang

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()
}