Menu
Coddy logo textTech

Strategy-Muster

Teil des Abschnitts Objektorientierte Programmierung der GO-Journey von Coddy — Lektion 90 von 107.

Das Strategy-Pattern ermöglicht es Ihnen, eine Familie von Algorithmen zu definieren, jeden einzelnen zu kapseln und sie zur Laufzeit austauschbar zu machen. Im Gegensatz zum Observer-Muster, bei dem mehrere Objekte auf ein Ereignis reagieren, erlaubt es Strategy einem einzelnen Objekt, dynamisch zwischen verschiedenen Verhaltensweisen zu wechseln.

In Go implementieren wir Strategy, indem wir ein Interface für den Algorithmus definieren und verschiedene Implementierungen in ein Context-Struct injizieren:

type PaymentStrategy interface {
    Pay(amount float64) string
}

type CreditCard struct{}
func (c CreditCard) Pay(amount float64) string {
    return fmt.Sprintf("Paid %.2f via Credit Card", amount)
}

type PayPal struct{}
func (p PayPal) Pay(amount float64) string {
    return fmt.Sprintf("Paid %.2f via PayPal", amount)
}

Die Context-Struktur hält eine Referenz auf das Strategy-Interface, was es ermöglicht, den Algorithmus auszutauschen, ohne den Code des Contexts zu ändern:

type Checkout struct {
    strategy PaymentStrategy
}

func (c *Checkout) SetStrategy(s PaymentStrategy) {
    c.strategy = s
}

func (c *Checkout) ProcessPayment(amount float64) string {
    return c.strategy.Pay(amount)
}

Jetzt können Sie die Zahlungsmethode zur Laufzeit ändern:

checkout := &Checkout{}

checkout.SetStrategy(CreditCard{})
fmt.Println(checkout.ProcessPayment(100.00))  // 100.00 via Kreditkarte bezahlt

checkout.SetStrategy(PayPal{})
fmt.Println(checkout.ProcessPayment(50.00))   // 50.00 via PayPal bezahlt

Das Strategie-Muster (Strategy) ist ideal, wenn Sie mehrere Möglichkeiten haben, eine Operation auszuführen, und eine davon basierend auf Benutzereingaben, Konfigurationen oder Laufzeitbedingungen auswählen müssen. Häufige Anwendungsfälle sind Sortieralgorithmen, Kompressionsmethoden und Validierungsregeln.

challenge icon

Aufgabe

Einfach

Lassen Sie uns ein Textformatierungssystem mit dem Strategy-Pattern erstellen! Sie werden verschiedene Formatierungsstrategien entwickeln, die zur Laufzeit ausgetauscht werden können. Dies ermöglicht es demselben Textprozessor, verschiedene Ausgabestile zu erzeugen, ohne seinen Kerncode zu ändern.

Sie werden Ihren Code auf drei Dateien verteilen:

  • formatter.go: Definieren Sie Ihr Strategy-Interface und die konkreten Formatierungsstrategien.

    Erstellen Sie ein TextFormatter-Interface mit einer Methode Format(text string) string, die Text gemäß den Regeln der Strategie transformiert.

    Implementieren Sie drei Formatierungsstrategien:

    • UppercaseFormatter — wandelt Text in Großbuchstaben um
    • SnakeCaseFormatter — ersetzt Leerzeichen durch Unterstriche und wandelt Text in Kleinbuchstaben um
    • TitleFormatter — schreibt den ersten Buchstaben jedes Wortes groß (Wörter sind durch Leerzeichen getrennt)
  • processor.go: Erstellen Sie Ihr Kontext-Struct, das die Formatierungsstrategie verwendet.

    Erstellen Sie ein TextProcessor-Struct, das eine TextFormatter-Strategie hält. Fügen Sie diese Methoden hinzu:

    • SetFormatter(f TextFormatter), um die aktive Formatierungsstrategie zu ändern
    • Process(text string) string, die den aktuellen Formatter auf den Text anwendet

    Erstellen Sie außerdem einen NewTextProcessor()-Konstruktor, der einen Prozessor ohne initial gesetzten Formatter zurückgibt.

  • main.go: Demonstrieren Sie das Wechseln von Strategien zur Laufzeit.

    Lesen Sie einen Text ein und lesen Sie dann die Anzahl der Formatierungsoperationen. Lesen Sie für jede Operation den Formattyp (upper, snake oder title), setzen Sie den entsprechenden Formatter in Ihrem Prozessor, verarbeiten Sie den ursprünglichen Text und geben Sie das Ergebnis aus.

Die folgenden Eingaben werden bereitgestellt:

  • Zeile 1: Der zu formatierende Text
  • Zeile 2: Anzahl der Formatierungsoperationen
  • Folgende Zeilen: Formattyp für jede Operation

Zum Beispiel, gegeben:

hello world today
3
upper
snake
title

Ihre Ausgabe sollte sein:

HELLO WORLD TODAY
hello_world_today
Hello World Today

Und gegeben:

Go Programming Language
2
snake
upper

Ihre Ausgabe sollte sein:

go_programming_language
GO PROGRAMMING LANGUAGE

Und gegeben:

design patterns are useful
1
title

Ihre Ausgabe sollte sein:

Design Patterns Are Useful

Beachten Sie, wie derselbe TextProcessor völlig unterschiedliche Ausgaben erzeugt, nur indem seine Formatter-Strategie ausgetauscht wird. Der Prozessor weiß nicht und es ist ihm egal, welchen spezifischen Formatter er verwendet – er delegiert einfach an die Strategie, die gerade gesetzt ist!

Spickzettel

Das Strategy-Muster definiert eine Familie von Algorithmen, kapselt jeden einzelnen und macht sie zur Laufzeit austauschbar. Es ermöglicht einem einzelnen Objekt, dynamisch zwischen verschiedenen Verhaltensweisen zu wechseln.

Definieren Sie ein Interface für den Algorithmus:

type PaymentStrategy interface {
    Pay(amount float64) string
}

Implementieren Sie konkrete Strategien:

type CreditCard struct{}
func (c CreditCard) Pay(amount float64) string {
    return fmt.Sprintf("Paid %.2f via Credit Card", amount)
}

type PayPal struct{}
func (p PayPal) Pay(amount float64) string {
    return fmt.Sprintf("Paid %.2f via PayPal", amount)
}

Erstellen Sie ein Context-Struct, das eine Referenz auf das Strategy-Interface hält:

type Checkout struct {
    strategy PaymentStrategy
}

func (c *Checkout) SetStrategy(s PaymentStrategy) {
    c.strategy = s
}

func (c *Checkout) ProcessPayment(amount float64) string {
    return c.strategy.Pay(amount)
}

Strategien zur Laufzeit ändern:

checkout := &Checkout{}

checkout.SetStrategy(CreditCard{})
fmt.Println(checkout.ProcessPayment(100.00))  // Paid 100.00 via Credit Card

checkout.SetStrategy(PayPal{})
fmt.Println(checkout.ProcessPayment(50.00))   // Paid 50.00 via PayPal

Das Strategy-Muster ist ideal, wenn Sie mehrere Möglichkeiten haben, eine Operation auszuführen, und eine davon basierend auf Benutzereingaben, Konfigurationen oder Laufzeitbedingungen auswählen müssen.

Probier es selbst

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	reader := bufio.NewReader(os.Stdin)
	
	// Zu formatierenden Text lesen
	text, _ := reader.ReadString('\n')
	text = strings.TrimSpace(text)
	
	// Anzahl der Operationen lesen
	countStr, _ := reader.ReadString('\n')
	count, _ := strconv.Atoi(strings.TrimSpace(countStr))
	
	// Einen neuen Textprozessor erstellen
	processor := NewTextProcessor()
	
	// Jede Formatierungsoperation verarbeiten
	for i := 0; i < count; i++ {
		formatType, _ := reader.ReadString('\n')
		formatType = strings.TrimSpace(formatType)
		
		// TODO: Basierend auf formatType ("upper", "snake" oder "title"),
		// den entsprechenden Formatter auf dem Prozessor setzen
		// und den verarbeiteten Text ausgeben
		
	}
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung