Menu
Coddy logo textTech

Wzorzec kompozytu

Część sekcji Programowanie obiektowe ścieżki PHP w Coddy. Lekcja 80 z 91.

Wzorzec Kompozyt to strukturalny wzorzec projektowy, który pozwala łączyć obiekty w struktury drzewiaste, a następnie pracować z nimi tak, jakby były pojedynczymi obiektami. Doskonale nadaje się do przedstawiania hierarchii część–całość, w których pojedyncze obiekty i grupy obiektów mają być traktowane jednolicie.

Rozważ system plików. Folder może zawierać pliki i inne foldery.

Podczas obliczania łącznego rozmiaru chcesz wywoływać tę samą metodę zarówno dla plików, jak i folderów, a foldery powinny rekurencyjnie sumować rozmiary swojej zawartości. Wzorzec Kompozyt zapewnia płynne działanie, udostępniając liściom (plikom) i kompozytom (folderom) ten sam interfejs.

<?php
interface FileSystemItem {
    public function getName(): string;
    public function getSize(): int;
}

class File implements FileSystemItem {
    public function __construct(
        private string $name,
        private int $size
    ) {}
    
    public function getName(): string {
        return $this->name;
    }
    
    public function getSize(): int {
        return $this->size;
    }
}

class Folder implements FileSystemItem {
    private array $items = [];
    
    public function __construct(private string $name) {}
    
    public function getName(): string {
        return $this->name;
    }
    
    public function add(FileSystemItem $item): void {
        $this->items[] = $item;
    }
    
    public function getSize(): int {
        $total = 0;
        foreach ($this->items as $item) {
            $total += $item->getSize();
        }
        return $total;
    }
}

Teraz możesz tworzyć struktury drzewiaste i traktować je jednolicie:

<?php
$docs = new Folder("Documents");
$docs->add(new File("resume.pdf", 250));
$docs->add(new File("cover.docx", 50));

$images = new Folder("Images");
$images->add(new File("photo.jpg", 1500));

$root = new Folder("Root");
$root->add($docs);
$root->add($images);
$root->add(new File("readme.txt", 10));

echo $root->getSize();

Wynik:

1810

Kod klienta wywołuje getSize(), nie przejmując się tym, czy ma do czynienia z pojedynczym plikiem, czy z głęboko zagnieżdżoną strukturą folderów. Wzorzec obsługuje rekurencję wewnętrznie, dzięki czemu praca ze złożonymi hierarchiami jest łatwa.

challenge icon

Wyzwanie

Łatwy

Zbudujmy system schematu organizacyjnego z wykorzystaniem wzorca Composite. Firmy mają hierarchiczną strukturę, w której działy mogą zawierać pracowników i inne poddziały: to idealna struktura drzewa, w której chcesz jednolicie obliczać sumy dla całej organizacji.

Rozmieścisz swój kod w czterech plikach:

  • OrganizationUnit.php: Zdefiniuj interfejs OrganizationUnit, który będą implementować zarówno pojedynczy pracownicy, jak i działy. Powinien zawierać dwie metody: getName(): string i getSalary(): int. Ten wspólny interfejs pozwala traktować pojedynczych pracowników i całe działy w ten sam sposób podczas obliczania kosztów.
  • Employee.php: Dołącz interfejs OrganizationUnit i utwórz klasę Employee, która go implementuje. Pracownik jest węzłem liściowym w drzewie organizacji. Ma imię i wynagrodzenie, ale nie może zawierać innych jednostek. Użyj promocji konstruktora, aby przyjąć imię i wynagrodzenie, i zaimplementuj obie metody interfejsu.
  • Department.php: Dołącz interfejs OrganizationUnit i utwórz klasę Department, która go implementuje. Dział jest kompozytem, który może zawierać zarówno pracowników, jak i inne działy. Twój dział powinien:
    • Przyjmować nazwę w konstruktorze
    • Implementować getName(), aby zwracać nazwę działu
    • Mieć metodę add(OrganizationUnit $unit): void, która dodaje pracowników lub poddziały
    • Implementować getSalary(), aby zwracać łączne wynagrodzenie wszystkich zawartych w nim jednostek (rekurencyjnie sumując wszystko, co się w nim znajduje)
  • main.php: Dołącz pliki Employee i Department. Otrzymasz jedno wejście: ciąg JSON reprezentujący strukturę organizacji.

    JSON będzie mieć następujący format:

    {"Engineering": [{"name": "Alice", "salary": 80000}, {"name": "Bob", "salary": 75000}], "Sales": [{"name": "Carol", "salary": 65000}]}

    Każdy klucz to nazwa działu, a jego wartością jest tablica pracowników wraz z ich imionami i wynagrodzeniami.

    Utwórz główny Department o nazwie "Company". Dla każdego działu w JSON utwórz Department, dodaj do niego pracowników, a następnie dodaj ten dział do Company. Na koniec wyświetl łączne wynagrodzenie firmy w następującym formacie:

    Total Salary: [amount]

Zaletą wzorca Composite jest to, że wywołanie getSalary() na dziale Company automatycznie oblicza sumę wynagrodzeń ze wszystkich zagnieżdżonych działów i pracowników: kod klienta nie musi znać wewnętrznej struktury.

Spróbuj swoich sił

<?php
require_once 'Employee.php';
require_once 'Department.php';

// Odczytaj dane wejściowe JSON
$input = trim(fgets(STDIN));
$data = (array)json_decode($input, true);

// TODO: Utwórz główny obiekt Department o nazwie "Company"

// TODO: Przejdź przez każdy dział w danych JSON
// Dla każdego działu:
//   - Utwórz nowy obiekt Department
//   - Dodaj każdego pracownika do tego działu
//   - Dodaj dział do Company

// TODO: Wypisz łączną kwotę wynagrodzeń w formacie: Total Salary: [amount]

?>
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator PHP online