Menu
Coddy logo textTech

Wzorzec kompozytu

Część sekcji Programowanie obiektowe ścieżki Java w Coddy. Lekcja 75 z 87.

Wzorzec Kompozytowy to strukturalny wzorzec projektowy, który pozwala łączyć obiekty w struktury drzewiaste, aby reprezentować hierarchie całość–część. Umożliwia klientom jednolite traktowanie pojedynczych obiektów i kompozycji obiektów za pośrednictwem wspólnego interfejsu.

Pomyśl o systemie plików: folder może zawierać pliki i inne foldery, które mogą zawierać kolejne pliki i foldery. Niezależnie od tego, czy masz do czynienia z pojedynczym plikiem, czy z całą strukturą folderów, chcesz wykonywać operacje, takie jak „pobierz rozmiar”, w ten sam sposób. Wzorzec definiuje interfejs Component, obiekty Leaf (pojedyncze elementy) oraz obiekty Composite (kontenery zawierające elementy podrzędne):

interface FileComponent {
    void display(String indent);
    int getSize();
}

class File implements FileComponent {
    private String name;
    private int size;
    
    public File(String name, int size) {
        this.name = name;
        this.size = size;
    }
    
    public void display(String indent) {
        System.out.println(indent + name + " (" + size + "KB)");
    }
    
    public int getSize() {
        return size;
    }
}

class Folder implements FileComponent {
    private String name;
    private List<FileComponent> children = new ArrayList<>();
    
    public Folder(String name) {
        this.name = name;
    }
    
    public void add(FileComponent component) {
        children.add(component);
    }
    
    public void display(String indent) {
        System.out.println(indent + name + "/");
        for (FileComponent child : children) {
            child.display(indent + "  ");
        }
    }
    
    public int getSize() {
        return children.stream().mapToInt(FileComponent::getSize).sum();
    }
}

Kompozyt deleguje operacje swoim elementom potomnym, umożliwiając tworzenie struktur rekurencyjnych. Klienci wchodzą w interakcję z drzewem, nie wiedząc, czy pracują z liściem, czy z kompozytem:

Folder root = new Folder("Documents");
root.add(new File("resume.pdf", 150));

Folder photos = new Folder("Photos");
photos.add(new File("vacation.jpg", 2000));
root.add(photos);

root.display("");
System.out.println("Total: " + root.getSize() + "KB");

Wzorzec Composite idealnie nadaje się do przedstawiania hierarchii, takich jak schematy organizacyjne, komponenty interfejsu użytkownika czy systemy menu, w których grupy i pojedyncze elementy trzeba traktować identycznie.

challenge icon

Wyzwanie

Łatwy

Zbudujmy system schematu organizacyjnego z użyciem wzorca Composite! Utworzysz strukturę, w której działy mogą zawierać pracowników i inne poddziały, co pozwoli obliczać łączne wynagrodzenia i wyświetlać hierarchię w jednolity sposób — niezależnie od tego, czy przeglądasz pojedynczego pracownika, czy cały oddział.

Podzielisz kod na cztery pliki:

  • OrganizationComponent.java: Zdefiniuj interfejs komponentu, który będą implementować zarówno pracownicy, jak i działy. Powinien deklarować dwie metody: showDetails(String indent), która wyświetla komponent z odpowiednim wcięciem, oraz getSalary(), która zwraca łączne wynagrodzenie jako liczbę całkowitą.
  • Employee.java: Utwórz klasę liścia reprezentującą poszczególnych pracowników. Employee ma imię (String) i wynagrodzenie (int), ustawiane za pomocą konstruktora. Po wywołaniu showDetails powinna wypisać [indent][name]: $[salary]. Metoda getSalary po prostu zwraca wynagrodzenie pracownika.
  • Department.java: Utwórz klasę złożoną, która może zawierać zarówno pracowników, jak i poddziały. Department ma nazwę (String) i przechowuje listę elementów podrzędnych typu OrganizationComponent. Powinna mieć metodę add(OrganizationComponent component) do dodawania członków. Po wywołaniu showDetails wypisuje [indent][name] Department, a następnie wywołuje showDetails dla każdego elementu podrzędnego, zwiększając wcięcie (dodając dwie spacje). Metoda getSalary zwraca sumę wynagrodzeń wszystkich elementów podrzędnych.
  • Main.java: Zbuduj swoją organizację! Otrzymasz cztery dane wejściowe: imiona i wynagrodzenia dwóch pracowników oraz nazwę poddziału i dane jednego pracownika.

    Utwórz dział "Engineering" jako element główny. Dodaj do niego dwóch pracowników, używając dwóch pierwszych par imienia i wynagrodzenia. Następnie utwórz poddział, używając trzeciej danej wejściowej, dodaj do niego jednego pracownika, używając czwartej pary imienia i wynagrodzenia, a potem dodaj ten poddział do Engineering.

    Wywołaj showDetails("") dla działu Engineering, a następnie wypisz łączne wynagrodzenie w formacie Total Salary: $[amount].

Dane wejściowe otrzymasz w następującej kolejności: imię employee1 (String), wynagrodzenie employee1 (int), imię employee2 (String), wynagrodzenie employee2 (int), nazwa poddziału (String), imię employee3 (String), wynagrodzenie employee3 (int).

Na przykład dla danych wejściowych Alice, 75000, Bob, 65000, QA, Charlie, 55000 wynik będzie następujący:

Engineering Department
  Alice: $75000
  Bob: $65000
  QA Department
    Charlie: $55000
Total Salary: $195000

Zwróć uwagę, że te same metody showDetails i getSalary działają bezproblemowo zarówno wtedy, gdy są wywoływane dla pojedynczego pracownika, jak i dla całego działu zawierającego zagnieżdżone poddziały. Struktura złożona automatycznie obsługuje rekurencję, dzięki czemu możesz traktować całą hierarchię jednolicie!

Spróbuj swoich sił

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // Wczytaj dane wejściowe
        String emp1Name = scanner.nextLine();
        int emp1Salary = Integer.parseInt(scanner.nextLine());
        String emp2Name = scanner.nextLine();
        int emp2Salary = Integer.parseInt(scanner.nextLine());
        String subDeptName = scanner.nextLine();
        String emp3Name = scanner.nextLine();
        int emp3Salary = Integer.parseInt(scanner.nextLine());
        
        // TODO: Utwórz dział "Engineering" jako główny
        
        // TODO: Utwórz i dodaj dwóch pracowników do Engineering, używając danych emp1 i emp2
        
        // TODO: Utwórz poddział, używając subDeptName
        
        // TODO: Utwórz i dodaj jednego pracownika do poddziału, używając danych emp3
        
        // TODO: Dodaj poddział do Engineering
        
        // TODO: Wywołaj showDetails("") dla działu Engineering
        
        // 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 Java online