Menu
Coddy logo textTech

Wzorzec kompozytu

Część sekcji Programowanie obiektowe ścieżki C# w Coddy. Lekcja 61 z 70.

Wzorzec Composite to wzorzec strukturalny, który pozwala łączyć obiekty w struktury drzewiaste oraz traktować pojedyncze obiekty i grupy obiektów jednolicie. Doskonale sprawdza się przy przedstawianiu hierarchii całość-część, gdy chcesz pracować zarówno z pojedynczymi elementami, jak i kolekcjami za pośrednictwem tego samego interfejsu.

Wzorzec ma trzy kluczowe elementy: interfejs komponentu definiujący wspólne operacje, klasy liści reprezentujące pojedyncze obiekty oraz klasy złożone, które zawierają elementy podrzędne i implementują ten sam interfejs:

public interface IFileSystemItem
{
    string Name { get; }
    int GetSize();
}

public class File : IFileSystemItem
{
    public string Name { get; }
    private int _size;
    
    public File(string name, int size)
    {
        Name = name;
        _size = size;
    }
    
    public int GetSize() => _size;
}

public class Folder : IFileSystemItem
{
    public string Name { get; }
    private List<IFileSystemItem> _items = new List<IFileSystemItem>();
    
    public Folder(string name) => Name = name;
    
    public void Add(IFileSystemItem item) => _items.Add(item);
    
    public int GetSize() => _items.Sum(item => item.GetSize());
}

Piękno tego wzorca polega na tym, że klienci nie muszą wiedzieć, czy pracują z pojedynczym plikiem, czy z całą hierarchią folderów:

var root = new Folder("root");
root.Add(new File("doc.txt", 100));

var subFolder = new Folder("images");
subFolder.Add(new File("photo.jpg", 500));
subFolder.Add(new File("icon.png", 50));

root.Add(subFolder);

Console.WriteLine(root.GetSize());  // 650

Wzorzec Kompozyt jest idealny w przypadku systemów plików, drzew komponentów interfejsu użytkownika, schematów organizacyjnych lub dowolnej struktury, w której kontenery i ich zawartość powinny być traktowane w ten sam sposób. Upraszcza kod klienta, eliminując potrzebę rozróżniania prostych i złożonych elementów.

challenge icon

Wyzwanie

Łatwy

Zbudujmy system schematu organizacyjnego z użyciem wzorca Composite. Utworzysz strukturę, w której zarówno poszczególnych pracowników, jak i działy (zawierające pracowników lub inne działy) można traktować jednakowo, co pozwoli obliczyć łączny koszt wynagrodzeń na dowolnym poziomie hierarchii.

Rozmieścisz kod w trzech plikach:

  • OrgComponent.cs: Zdefiniuj interfejs IOrgComponent w przestrzeni nazw Organization z dwoma elementami: właściwością Name (string) i metodą GetSalary(), która zwraca liczbę całkowitą. Ten interfejs określa wspólny kontrakt, którego będą przestrzegać zarówno poszczególni pracownicy, jak i działy.
  • OrgClasses.cs: Utwórz dwie klasy w tej samej przestrzeni nazw:
    • Employee — klasę liścia reprezentującą pojedynczego pracownika. Przyjmuje ona imię i wynagrodzenie w konstruktorze oraz implementuje interfejs, zwracając własne wynagrodzenie.
    • Department — klasę złożoną reprezentującą grupę. Przyjmuje ona nazwę w konstruktorze i przechowuje listę elementów IOrgComponent. Dodaj metodę Add(IOrgComponent component), aby dodawać członków. Metoda GetSalary() powinna zwracać sumę wynagrodzeń wszystkich członków działu.
  • Program.cs: Zbuduj strukturę organizacji na podstawie danych wejściowych i oblicz łączne wynagrodzenie. Utworzysz działy i pracowników, odpowiednio umieścisz ich w hierarchii i pokażesz, jak wzorzec Composite pozwala traktować całą hierarchię jednakowo.

Otrzymasz następujące dane wejściowe:

  • Liczbę komponentów do utworzenia
  • Dla każdego komponentu: typ (employee lub department), następnie nazwę, a w przypadku pracowników także ich wynagrodzenie. W przypadku działów otrzymasz również nazwę działu nadrzędnego (lub root, jeśli jest to dział najwyższego poziomu).

Format danych wejściowych dla każdego komponentu:

employee
{name}
{salary}
{parent_department}

department
{name}
{parent_department}

Po zbudowaniu struktury wyświetl łączne wynagrodzenie działu głównego w formacie {root_name} Total Salary: {amount}.

Na przykład, jeśli dane wejściowe są następujące:

5
department
Engineering
root
employee
Alice
5000
Engineering
employee
Bob
4500
Engineering
department
QA
Engineering
employee
Charlie
4000
QA

Wynik powinien być następujący:

Engineering Total Salary: 13500

Dział Engineering zawiera Alice (5000), Boba (4500) oraz dział QA. Dział QA zawiera Charliego (4000). Po wywołaniu metody GetSalary() dla działu Engineering metoda rekurencyjnie oblicza łączną kwotę na podstawie wszystkich zagnieżdżonych komponentów — pokazując, jak wzorzec Composite pozwala pracować ze złożonymi strukturami drzewiastymi za pomocą prostego, jednolitego interfejsu!

Spróbuj swoich sił

using System;
using System.Collections.Generic;
using Organization;

class Program
{
    public static void Main(string[] args)
    {
        int n = Convert.ToInt32(Console.ReadLine());
        
        // TODO: Utwórz słownik do przechowywania działów według nazwy
        // Ułatwi to znalezienie działów nadrzędnych podczas dodawania komponentów
        Dictionary<string, Department> departments = new Dictionary<string, Department>();
        
        // TODO: Śledź dział główny
        Department root = null;
        
        for (int i = 0; i < n; i++)
        {
            string type = Console.ReadLine();
            
            if (type == "department")
            {
                string name = Console.ReadLine();
                string parent = Console.ReadLine();
                
                // TODO: Utwórz dział
                // TODO: Dodaj go do słownika departments
                // TODO: Jeśli parent ma wartość "root", jest to dział główny
                // TODO: W przeciwnym razie znajdź dział nadrzędny i dodaj do niego ten dział
            }
            else if (type == "employee")
            {
                string name = Console.ReadLine();
                int salary = Convert.ToInt32(Console.ReadLine());
                string parent = Console.ReadLine();
                
                // TODO: Utwórz pracownika
                // TODO: Znajdź dział nadrzędny i dodaj do niego pracownika
            }
        }
        
        // TODO: Wyświetl łączną kwotę wynagrodzeń w dziale głównym
        // Format: "{root_name} Total Salary: {amount}"
        Console.WriteLine($"{root.Name} Total Salary: {root.GetSalary()}");
    }
}
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 C# online