1. Co to jest wzorzec Flyweight
1.1 Definicja i Koncept
Wzorzec Flyweight to wzorzec projektowy strukturalny, którego głównym celem jest minimalizacja liczby współdzielonych obiektów, co pozwala zaoszczędzić pamięć i poprawić wydajność. Wzorzec Flyweight zmniejsza tworzenie i zużycie obiektów poprzez współdzielenie tych samych lub podobnych obiektów, osiągając optymalizację wydajności.
1.2 Różnice w porównaniu z innymi wzorcami projektowymi
W porównaniu z innymi wzorcami projektowymi, wzorzec Flyweight skupia się głównie na współdzieleniu i ponownym użyciu obiektów. Dzieli obiekty na współdzielone stany wewnętrzne i niewspółdzielone stany zewnętrzne. Poprzez współdzielenie stanów wewnętrznych zmniejsza tworzenie i zużycie pamięci obiektów, poprawiając wydajność systemu.
2. Charakterystyka i Zalety wzorca Flyweight
Główne cechy i zalety wzorca Flyweight obejmują:
- Zminimalizowane użycie pamięci: Redukuje zużycie pamięci poprzez współdzielenie tych samych lub podobnych obiektów.
- Poprawiona wydajność: Zmniejsza tworzenie i niszczenie obiektów, przyspieszając działanie systemu.
- Wsparcie dla dużej liczby drobnoziarnistych obiektów: Może tworzyć dużą liczbę drobnoziarnistych obiektów, nie zajmując przy tym zbyt dużo miejsca w pamięci.
- Uproszczona struktura systemu: Poprzez oddzielenie stanów wewnętrznych i zewnętrznych obiektów, upraszcza strukturę i złożoność systemu.
3. Przykłady praktycznych zastosowań wzorca Flyweight
Wzorzec Flyweight można zastosować w następujących scenariuszach:
- Obiekty cząsteczkowe w grach: Właściwości każdego obiektu cząsteczki można podzielić na stany wewnętrzne i zewnętrzne, a obiekty cząsteczek o tych samych właściwościach mogą być współdzielone.
- Obiekty połączeń w serwerach sieciowych: Właściwości obiektów połączeń można podzielić na stany wewnętrzne i zewnętrzne, a istniejące obiekty połączeń mogą być ponownie wykorzystane przed ich odzyskaniem.
4. Implementacja wzorca Flyweight w języku Golang
4.1 Diagram klas UML
Diagram klas UML wzorca Flyweight w języku Golang wygląda następująco:
4.2 Wprowadzenie przykładu
W tym przykładzie stworzymy edytor graficzny oparty na wzorcu Flyweight, zawierający koła różnych kolorów i redukując zużycie pamięci poprzez współdzielenie obiektów okręgów o tym samym kolorze.
4.3 Kroki implementacji
4.3.1 Utwórz interfejs Flyweight i klasę ConcreteFlyweight
Najpierw musimy utworzyć interfejs Flyweight
w celu zdefiniowania operacji obiektów współdzielonych. Następnie można utworzyć klasę ConcreteFlyweight
, aby zaimplementować interfejs Flyweight
i zawierać stany wewnętrzne.
// Flyweight definiuje interfejs obiektów flyweight
type Flyweight interface {
Operation(extrinsicState string)
}
// ConcreteFlyweight reprezentuje konkretne obiekty flyweight, implementując interfejs Flyweight
type ConcreteFlyweight struct {
intrinsicState string
}
// Operation implementuje metodę operacji współdzielonego obiektu
func (f *ConcreteFlyweight) Operation(extrinsicState string) {
fmt.Printf("Konkretny obiekt flyweight, stan wewnętrzny: %s, stan zewnętrzny: %s\n", f.intrinsicState, extrinsicState)
}
4.3.2 Utwórz klasę FlyweightFactory
Następnie możemy utworzyć klasę FlyweightFactory
do zarządzania i współdzielenia obiektów flyweight. Ta klasa fabryki przechowuje słownik flyweights
do przechowywania utworzonych obiektów flyweight.
// Klasa FlyweightFactory
type FlyweightFactory struct {
flyweights map[string]Flyweight
}
// GetFlyweight pobiera lub tworzy obiekt flyweight z fabryki
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 Przykład wywołania klienta
Na koniec możemy utworzyć klasę Client
, aby pokazać, jak używać wzorca flyweight do implementacji edytora graficznego.
// Klasa Klient
type Client struct {
flyweight Flyweight
}
// Operacja wykonuje operację
func (c *Client) Operation() {
c.flyweight.Operation("stan zewnętrzny")
}
4.4 Względy implementacyjne i najlepsze praktyki
4.4.1 Dzielenie stanu i bezpieczeństwo wątków
Podczas korzystania z wzorca flyweight należy zwrócić uwagę na udostępnianie wewnętrznego stanu i bezpieczeństwo wątków. Ponieważ obiekty flyweight są współdzielone przez wielu klientów, konieczne jest zapewnienie spójności wewnętrznego stanu.
4.4.2 Zarządzanie puli obiektów
Aby lepiej zarządzać i ponownie użyć obiektów flyweight, można użyć pul obiektów do przechowywania stworzonych obiektów flyweight. Pule obiektów mogą zwiększyć wskaźnik ponownego użycia obiektów i zmniejszyć narzut tworzenia i usuwania obiektów.
4.4.3 Zewnętrzne zarządzanie stanem obiektu
Wzorzec flyweight separuje wewnętrzny stan i zewnętrzny stan obiektów, ale zewnętrzny stan musi być zarządzany przez klienta. Korzystając z obiektów flyweight, klient musi przekazać zewnętrzny stan do obiektu flyweight dla operacji.
Pełny przykład kodu
Poniżej znajduje się kompletny przykład kodu w języku Go:
package main
import "fmt"
// Flyweight definiuje interfejs obiektu flyweight
type Flyweight interface {
Operation(extrinsicState string)
}
// ConcreteFlyweight reprezentuje konkretny obiekt flyweight i implementuje interfejs Flyweight
type ConcreteFlyweight struct {
intrinsicState string
}
// Operation implementuje metodę operacji dla udostępnianych obiektów
func (f *ConcreteFlyweight) Operation(extrinsicState string) {
fmt.Printf("Konkretny obiekt flyweight, wewnętrzny stan: %s, zewnętrzny stan: %s\n", f.intrinsicState, extrinsicState)
}
// Klasa FlyweightFactory
type FlyweightFactory struct {
flyweights map[string]Flyweight
}
// GetFlyweight pobiera lub tworzy obiekt flyweight z fabryki
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
}
// Klasa Klient
type Client struct {
flyweight Flyweight
}
// Operacja wykonuje operację
func (c *Client) Operation() {
c.flyweight.Operation("zewnętrzny stan")
}
func main() {
factory := &FlyweightFactory{
flyweights: make(map[string]Flyweight),
}
flyweight1 := factory.GetFlyweight("A")
flyweight1.Operation("zewnętrzny stan 1")
flyweight2 := factory.GetFlyweight("B")
flyweight2.Operation("zewnętrzny stan 2")
client := &Client{
flyweight: factory.GetFlyweight("A"),
}
client.Operation()
}