1. Was ist das Fliegengewichtsmuster
1.1 Definition und Konzept
Das Fliegengewichtsmuster ist ein strukturelles Entwurfsmuster, dessen Hauptzweck darin besteht, die Anzahl der gemeinsam genutzten Objekte zu minimieren, um so Speicher zu sparen und die Leistung zu verbessern. Das Fliegengewichtsmuster reduziert die Erstellung und Verwendung von Objekten, indem es dieselben oder ähnliche Objekte gemeinsam nutzt und so eine Leistungsoptimierung erreicht.
1.2 Unterschied zu anderen Entwurfsmustern
Im Vergleich zu anderen Entwurfsmustern konzentriert sich das Fliegengewichtsmuster hauptsächlich auf das Teilen und Wiederverwenden von Objekten. Es unterteilt Objekte in teilbare interne Zustände und nicht teilbare externe Zustände. Durch das Teilen interner Zustände reduziert es die Erstellung und den Speicherverbrauch von Objekten und verbessert so die Effizienz des Systems.
2. Merkmale und Vorteile des Fliegengewichtsmusters
Die Hauptmerkmale und Vorteile des Fliegengewichtsmusters sind:
- Minimierter Speicherverbrauch: Es reduziert den Speicherverbrauch, indem es dieselben oder ähnliche Objekte gemeinsam nutzt.
- Verbesserte Leistung: Es verringert die Erstellung und Zerstörung von Objekten und beschleunigt so den Betrieb des Systems.
- Unterstützung für eine große Anzahl feingranularer Objekte: Es kann eine große Anzahl feingranularer Objekte erstellen, ohne zu viel Speicherplatz zu belegen.
- Vereinfachte Systemstruktur: Durch die Trennung der internen und externen Zustände von Objekten vereinfacht es die Struktur und Komplexität des Systems.
3. Beispiele für praktische Anwendungen des Fliegengewichtsmusters
Das Fliegengewichtsmuster kann in folgenden Szenarien angewendet werden:
- Partikelobjekte in Spielen: Die Eigenschaften jedes Partikelobjekts können in interne und externe Zustände unterteilt werden, und Partikelobjekte mit denselben Eigenschaften können gemeinsam genutzt werden.
- Verbindungsobjekte in Netzwerkservern: Die Eigenschaften von Verbindungsobjekten können in interne und externe Zustände unterteilt werden, und vorhandene Verbindungsobjekte können wiederverwendet werden, bevor sie recycled werden.
4. Implementierung des Fliegengewichtsmusters in Golang
4.1 UML Klassendiagramm
Das UML-Klassendiagramm des Fliegengewichtsmusters in Golang sieht wie folgt aus:
4.2 Beispiel Einführung
In diesem Beispiel erstellen wir einen Grafikeditor auf Basis des Fliegengewichtsmusters, der Kreise unterschiedlicher Farben enthält, und reduzieren den Speicherverbrauch, indem wir Kreisobjekte mit derselben Farbe gemeinsam nutzen.
4.3 Umsetzungsschritte
4.3.1 Erstellung der Fliegengewichtsschnittstelle und der konkreten Fliegengewichtsklasse
Zuerst müssen wir eine Fliegengewicht
-Schnittstelle erstellen, um die Operationen von gemeinsam genutzten Objekten zu definieren. Anschließend können wir eine ConcreteFlyweight
-Klasse erstellen, um die Flyweight
-Schnittstelle zu implementieren und interne Zustände einzuschließen.
// Flyweight definiert die Schnittstelle der Fliegengewichtsobjekte
type Flyweight interface {
Operation(extrinsicState string)
}
// ConcreteFlyweight repräsentiert das konkrete Fliegengewichtsobjekt, das die Flyweight-Schnittstelle implementiert
type ConcreteFlyweight struct {
intrinsicState string
}
// Operation implementiert die Operation des gemeinsam genutzten Objekts
func (f *ConcreteFlyweight) Operation(extrinsicState string) {
fmt.Printf("Konkretes Fliegengewichtsobjekt, interner Zustand: %s, externer Zustand: %s\n", f.intrinsicState, extrinsicState)
}
4.3.2 Erstellung der FlyweightFactory-Klasse
Anschließend können wir eine FlyweightFactory
-Klasse erstellen, um Fliegengewichtsobjekte zu verwalten und gemeinsam zu nutzen. Diese Factory-Klasse verwaltet ein Fliegengewichte
-Wörterbuch, um die erstellten Fliegengewichtsobjekte zu speichern.
// FlyweightFactory-Klasse
type FlyweightFactory struct {
flyweights map[string]Flyweight
}
// GetFlyweight ruft ein Fliegengewichtsobjekt aus der Factory ab oder erstellt es
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 Beispiel für einen Clientaufruf
Schließlich können wir eine Client
-Klasse erstellen, um zu demonstrieren, wie das Fliegengewichtsmuster zur Implementierung eines Grafikeditors verwendet wird.
// Client-Klasse
type Client struct {
flyweight Flyweight
}
// Operation führt eine Operation aus
func (c *Client) Operation() {
c.flyweight.Operation("externer Zustand")
}
4.4 Implementierungserwägungen und bewährte Praktiken
4.4.1 Zustandsfreigabe und Thread-Sicherheit
Bei der Verwendung des Fliegengewichtsmusters muss auf die Freigabe des internen Zustands und die Thread-Sicherheit geachtet werden. Da Fliegengewichtsobjekte von mehreren Clients gemeinsam genutzt werden, ist es notwendig, die Konsistenz des internen Zustands sicherzustellen.
4.4.2 Verwaltung des Objektpools
Zur besseren Verwaltung und Wiederverwendung von Fliegengewichtsobjekten können Objektpools verwendet werden, um die erstellten Fliegengewichtsobjekte zu speichern. Objektpools können die Wiederverwendungsrate von Objekten erhöhen und den Overhead der Objekterstellung und -zerstörung reduzieren.
4.4.3 Externe Verwaltung des Objektzustands
Das Fliegengewichtsmuster trennt den internen Zustand und den externen Zustand von Objekten, aber der externe Zustand muss vom Client verwaltet werden. Bei der Verwendung von Fliegengewichtsobjekten muss der Client den externen Zustand an das Fliegengewichtsobjekt für den Betrieb übergeben.
Vollständiges Codebeispiel
Im Folgenden finden Sie ein vollständiges Golang-Codebeispiel:
package main
import "fmt"
// Flyweight definiert die Schnittstelle eines Fliegengewichtsobjekts
type Flyweight interface {
Operation(extrinsicState string)
}
// ConcreteFlyweight repräsentiert ein spezifisches Fliegengewichtsobjekt und implementiert die Flyweight-Schnittstelle
type ConcreteFlyweight struct {
intrinsicState string
}
// Operation implementiert die Operation-Methode für gemeinsam genutzte Objekte
func (f *ConcreteFlyweight) Operation(extrinsicState string) {
fmt.Printf("Konkretes Fliegengewichtsobjekt, interner Zustand: %s, externer Zustand: %s\n", f.intrinsicState, extrinsicState)
}
// FlyweightFactory-Klasse
type FlyweightFactory struct {
flyweights map[string]Flyweight
}
// GetFlyweight ruft ein Fliegengewichtsobjekt aus der Fabrik ab oder erstellt es
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
}
// Client-Klasse
type Client struct {
flyweight Flyweight
}
// Operation führt eine Operation durch
func (c *Client) Operation() {
c.flyweight.Operation("externer Zustand")
}
func main() {
factory := &FlyweightFactory{
flyweights: make(map[string]Flyweight),
}
flyweight1 := factory.GetFlyweight("A")
flyweight1.Operation("externer Zustand 1")
flyweight2 := factory.GetFlyweight("B")
flyweight2.Operation("externer Zustand 2")
client := &Client{
flyweight: factory.GetFlyweight("A"),
}
client.Operation()
}