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.
Wyzwanie
ŁatwyZbudujmy 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, orazgetSalary(), która zwraca łączne wynagrodzenie jako liczbę całkowitą.Employee.java: Utwórz klasę liścia reprezentującą poszczególnych pracowników.Employeema imię (String) i wynagrodzenie (int), ustawiane za pomocą konstruktora. Po wywołaniushowDetailspowinna wypisać[indent][name]: $[salary]. MetodagetSalarypo 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.Departmentma nazwę (String) i przechowuje listę elementów podrzędnych typuOrganizationComponent. Powinna mieć metodęadd(OrganizationComponent component)do dodawania członków. Po wywołaniushowDetailswypisuje[indent][name] Department, a następnie wywołujeshowDetailsdla każdego elementu podrzędnego, zwiększając wcięcie (dodając dwie spacje). MetodagetSalaryzwraca 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 formacieTotal 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: $195000Zwróć 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]
}
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy programowania obiektowego
Pliki zewnętrzneWprowadzenie do programowania obiektowegoKlasy a obiektySłowo kluczowe thisMetodyPola (atrybuty)Metoda konstruktoraPrzeciążanie konstruktoraPodsumowanie — prosty kalkulator4Dziedziczenie
Podstawy dziedziczenia (extends)Słowo kluczowe superPrzesłanianie metod (@Override)Łańcuch wywołań konstruktorówKlasa ObjectDziedziczenie jedno- i wielopoziomoweDlaczego Java nie obsługuje wielodziedziczenia klasPodsumowanie – hierarchia pracowników7Metody specjalne i klasa Object
Metoda toString()equals() i hashCode()Metoda clone()compareTo() i ComparableInterfejs ComparatorPodsumowanie — własne sortowanie10Obsługa wyjątków w programowaniu obiektowym
Hierarchia klas wyjątkówWłasne wyjątkiBłędy sprawdzane a niesprawdzaneWzorzec try-with-resourcesPodsumowanie – zweryfikowany użytkownik2Modyfikatory dostępu i enkapsulacja
Przegląd poziomów dostępuMetody pobierające i ustawiająceUkrywanie informacjiSłowo kluczowe finalPodsumowanie — menedżer kont bankowych5Polimorfizm
Podstawy przeciążania metodPrzesłanianie metod (w czasie wykonywania)Rzutowanie w górę i w dółOperator instanceofKlasy i metody abstrakcyjnePowtórka — kalkulator figur8Zaawansowane koncepcje OOP
Kompozycja a dziedziczenieAgregacja a kompozycjaKlasy wewnętrzne, zagnieżdżone i anonimoweEnumy i metody enumówRekordy (Java 16+)Klasy zapieczętowane (Java 17+)11Wzorce projektowe — część 1
Wprowadzenie do wzorców projektowychWzorzec SingletonWzorzec FactoryWzorzec BuilderWzorzec ObserverWzorzec Strategy3Właściwości klas i składowe statyczne
Zmienne instancji a zmienne statyczneMetody statyczneBloki statyczneStałe (static final)Powtórka – Counter i Utility6Interfejsy i klasy abstrakcyjne
Wprowadzenie do interfejsówImplementowanie interfejsówImplementacja wielu interfejsówMetody domyślne i statyczne w interfejsieKlasy abstrakcyjne a interfejsyInterfejsy funkcyjnePodsumowanie — system płatności9Typy generyczne
Wprowadzenie do typów generycznychKlasy generyczneMetody generyczneOgraniczone parametry typówSymbole wieloznaczne (?, extends, super)Powtórzenie – kontener generyczny12Wzorce projektowe, część 2
Wzorzec poleceniaWzorzec adapteraWzorzec dekoratoraWzorzec metody szablonowejWzorzec stanuWzorzec kompozytuWzorzec iteratoraPoćwicz samodzielnie: Kompilator Java online