Menu
Coddy logo textTech

Composite-Muster

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

Das Composite-Muster ermöglicht es dir, einzelne Objekte und Objektgruppen einheitlich zu behandeln. Während State das Verhalten auf Grundlage des internen Zustands ändert, erstellt Composite Baumstrukturen, in denen sowohl Blätter als auch Container dieselbe Schnittstelle verwenden.

Dieses Muster eignet sich perfekt für hierarchische Strukturen wie Dateisysteme, Organigramme oder UI-Komponenten. In Go definieren wir eine gemeinsame Schnittstelle, die sowohl einzelne Elemente als auch Container implementieren:

type Component interface {
    GetSize() int
}

type File struct {
    Name string
    Size int
}

func (f File) GetSize() int {
    return f.Size
}

Das zusammengesetzte Objekt (der Container) enthält untergeordnete Objekte und implementiert dieselbe Schnittstelle, indem es deren Ergebnisse aggregiert:

type Folder struct {
    Name     string
    Children []Component
}

func (f *Folder) Add(c Component) {
    f.Children = append(f.Children, c)
}

func (f Folder) GetSize() int {
    total := 0
    for _, child := range f.Children {
        total += child.GetSize()
    }
    return total
}

Jetzt kannst du Ordner in Ordnern verschachteln, und der Aufruf von GetSize() funktioniert identisch, unabhängig davon, ob es sich um eine einzelne Datei oder einen gesamten Verzeichnisbaum handelt:

docs := &Folder{Name: "docs"}
docs.Add(File{Name: "readme.txt", Size: 100})
docs.Add(File{Name: "notes.txt", Size: 50})

root := &Folder{Name: "root"}
root.Add(docs)
root.Add(File{Name: "config.json", Size: 25})

fmt.Println(root.GetSize())  // 175

Composite ist ideal, wenn du Teil-Ganzes-Hierarchien darstellen musst und möchtest, dass Clients einzelne Objekte und Kompositionen identisch behandeln.

challenge icon

Aufgabe

Einfach

Erstellen wir ein Organigramm-System mithilfe des Composite-Musters! Du wirst eine Struktur erstellen, in der sowohl einzelne Mitarbeiter als auch Abteilungen (die Mitarbeiter oder andere Abteilungen enthalten) einheitlich behandelt werden können – perfekt, um die Gesamtgehälter für jeden Teil der Organisation zu berechnen.

Du wirst deinen Code auf drei Dateien verteilen:

  • component.go: Definiere die gemeinsame Schnittstelle, die sowohl einzelne Mitarbeiter als auch Gruppen implementieren werden.

    Erstelle eine OrgComponent-Schnittstelle mit zwei Methoden:

    • GetSalary() int: gibt das Gesamtgehalt dieser Komponente zurück
    • GetName() string: gibt den Namen des Mitarbeiters oder der Abteilung zurück
  • organization.go: Implementiere sowohl den Blatt-Typ (einzelner Mitarbeiter) als auch den Komposit-Typ (Abteilung).

    Erstelle eine Employee-Struktur mit den Feldern Name (string) und Salary (int). Die Methode GetSalary() gibt das eigene Gehalt zurück, und GetName() gibt den eigenen Namen zurück.

    Erstelle eine Department-Struktur mit einem Name-Feld (string) und einem Members-Slice, der OrgComponent-Elemente enthält. Füge eine Add(c OrgComponent)-Methode hinzu, um Mitglieder hinzuzufügen. Die Methode GetSalary() soll die Summe der Gehälter aller Mitglieder zurückgeben, und GetName() gibt den Namen der Abteilung zurück.

  • main.go: Erstelle eine Organisationsstruktur und berechne die Gehälter.

    Lies die Anzahl der Mitarbeiter ein. Lies für jeden Mitarbeiter seinen Namen und sein Gehalt ein und erstelle Employee-Instanzen. Lies anschließend die Anzahl der Abteilungen ein. Lies für jede Abteilung ihren Namen und die Anzahl der Mitgliederindizes ein, gefolgt von diesen Indizes (0-basiert; sie beziehen sich in der Reihenfolge ihrer Erstellung auf zuvor erstellte Mitarbeiter oder Abteilungen).

    Lies nach dem Erstellen der Struktur einen abschließenden Index ein und gib den Namen und das Gesamtgehalt dieser Komponente im Format [name]: [salary] aus.

Die folgenden Eingaben werden bereitgestellt:

  • Anzahl der Mitarbeiter, anschließend der Name und das Gehalt jedes Mitarbeiters in separaten Zeilen
  • Anzahl der Abteilungen, anschließend der Name jeder Abteilung, die Anzahl ihrer Mitglieder und die Mitgliederindizes
  • Abschließender Index für die Abfrage

Zum Beispiel bei:

3
Alice
50000
Bob
60000
Carol
55000
1
Engineering
3
0
1
2
3

Deine Ausgabe sollte sein:

Engineering: 165000

Und bei:

4
Alice
50000
Bob
60000
Carol
55000
Dave
70000
2
Dev
2
0
1
QA
2
2
3
4

Deine Ausgabe sollte sein:

QA: 125000

Und bei:

2
Alice
50000
Bob
60000
0
1

Deine Ausgabe sollte sein:

Bob: 60000

Beachte, wie GetSalary() identisch funktioniert, unabhängig davon, ob du die Methode für einen einzelnen Mitarbeiter oder eine gesamte Abteilung aufrufst: Das Composite-Muster ermöglicht es dir, einzelne Elemente und Gruppen über dieselbe Schnittstelle einheitlich zu behandeln!

Probier es selbst

package main

import "fmt"

func main() {
	// Lies die Anzahl der Mitarbeiter
	var numEmployees int
	fmt.Scanln(&numEmployees)

	// Speichere alle Komponenten (Mitarbeiter und Abteilungen) in der Reihenfolge der Erstellung
	var components []OrgComponent

	// TODO: Lies den Namen und das Gehalt jedes Mitarbeiters
	// Erstelle Employee-Instanzen und füge sie dem components-Slice hinzu
	for i := 0; i < numEmployees; i++ {
		var name string
		var salary int
		fmt.Scanln(&name)
		fmt.Scanln(&salary)
		// TODO: Erstelle employee und füge zu components hinzu
	}

	// Lies die Anzahl der Abteilungen
	var numDepartments int
	fmt.Scanln(&numDepartments)

	// TODO: Lies den Namen, die Mitgliederanzahl und die Mitgliederindizes jeder Abteilung
	// Erstelle Department-Instanzen, füge Mitglieder nach Index hinzu und füge zu components hinzu
	for i := 0; i < numDepartments; i++ {
		var deptName string
		var memberCount int
		fmt.Scanln(&deptName)
		fmt.Scanln(&memberCount)
		// TODO: Erstelle department, lies Mitgliederindizes, füge Mitglieder hinzu, füge zu components hinzu
	}

	// Lies den finalen Index für die Abfrage
	var queryIndex int
	fmt.Scanln(&queryIndex)

	// TODO: Gib den Namen und das Gesamtgehalt der Komponente im Format aus: [name]: [salary]
	// Beispiel: fmt.Printf("%s: %d\n", name, salary)
}
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

Übe selbstständig: Online-Go-Compiler