Menu
Coddy logo textTech

Kompozit Kalıbı

Coddy'nin C++ Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası — ders 101 / 104.

Composite deseni, tekil nesneleri ve nesne gruplarını tek tipte ele almanıza olanak tanır. Nesneleri, hem tekil öğelerin hem de öğe kapsayıcılarının aynı arayüzü paylaştığı ağaç yapılarında birleştirir. Bu; dosya sistemleri, organizasyon şemaları veya kullanıcı arayüzü bileşenleri gibi hiyerarşileri temsil etmek için idealdir.

Desen üç ana bölümden oluşur: ortak işlemleri tanımlayan bir Component arayüzü, tekil nesneleri temsil eden Leaf sınıfları ve alt öğeleri içeren ve işlemleri onlara devreden Composite sınıfları:

#include <iostream>
#include <memory>
#include <vector>
#include <string>

// Bileşen arayüzü
class FileSystemItem {
public:
    virtual void display(int indent = 0) const = 0;
    virtual int getSize() const = 0;
    virtual ~FileSystemItem() = default;
};

// Yaprak (Leaf) - bireysel dosyaları temsil eder
class File : public FileSystemItem {
    std::string name;
    int size;
public:
    File(const std::string& n, int s) : name(n), size(s) {}
    
    void display(int indent = 0) const override {
        std::cout << std::string(indent, ' ') << name 
                  << " (" << size << " KB)\n";
    }
    int getSize() const override { return size; }
};

// Kompozit (Composite) - diğer bileşenleri içerir
class Folder : public FileSystemItem {
    std::string name;
    std::vector<std::shared_ptr<FileSystemItem>> children;
public:
    Folder(const std::string& n) : name(n) {}
    
    void add(std::shared_ptr<FileSystemItem> item) {
        children.push_back(item);
    }
    
    void display(int indent = 0) const override {
        std::cout << std::string(indent, ' ') << "[" << name << "]\n";
        for (const auto& child : children) {
            child->display(indent + 2);
        }
    }
    
    int getSize() const override {
        int total = 0;
        for (const auto& child : children) {
            total += child->getSize();
        }
        return total;
    }
};

Folder bileşeni alt öğeleri saklar ve bunlar üzerinde döngü kurarak işlemleri gerçekleştirir. Bir klasör üzerinde getSize() metodunu çağırdığınızda, içerdiği tüm öğelerin toplam boyutunu özyinelemeli olarak hesaplar. İstemci kodunun bir dosya mı yoksa bir klasörle mi çalıştığını bilmesine gerek yoktur; her ikisi de aynı arayüze yanıt verir.

Parça-bütün hiyerarşilerini temsil etmeniz gerektiğinde ve istemcilerin tekil nesneleri ve kompozisyonları tek tipte ele almasını istediğinizde Composite kullanın.

challenge icon

Görev

Kolay

Composite desenini kullanarak bir Organizasyon Şeması sistemi oluşturalım. Hem bireysel çalışanların hem de departmanların (diğer çalışanları veya alt departmanları içeren) tek tip olarak ele alınabildiği bir hiyerarşi oluşturacaksınız. Bu, gerçek şirketlerin nasıl yapılandırıldığını yansıtır; departmanlar insanları ve diğer departmanları içererek bir ağaç yapısı oluşturur.

Kodunuzu üç dosya halinde düzenleyeceksiniz:

  • OrgComponent.h: Hem çalışanların hem de departmanların uygulayacağı bileşen arayüzünü tanımlayın.

    Şu özelliklere sahip soyut bir OrgComponent sınıfı oluşturun:

    • getName() — bileşenin adını döndürür
    • getSalary() — toplam maaşı döndürür (çalışanlar için kendi maaşları; departmanlar için içerilen tüm maaşların toplamı)
    • display(int indent = 0) — bileşeni uygun girintiyle görüntüler

    Sanal bir yıkıcı (virtual destructor) ekleyin.

  • Organization.h: Yaprak (leaf) ve bileşik (composite) sınıfları uygulayın.

    Ad ve maaş bilgilerini saklayan bir Employee sınıfı (yaprak) oluşturun. Bu sınıfın display() metodu, çalışan bilgilerini şu formatta yazdırmalıdır:

    [indent spaces]- [name] ($[salary])

    Bir ad ve std::shared_ptr kullanarak OrgComponent çocuklarından oluşan bir koleksiyon saklayan bir Department sınıfı (bileşik) oluşturun. Şunları uygulayın:

    • add(std::shared_ptr<OrgComponent> component) — departmana bir çocuk ekler
    • getSalary() — tüm üyelerin toplam maaşını özyinelemeli (recursive) olarak hesaplar
    • display() — departman adını köşeli parantez içinde yazdırır, ardından tüm çocukları artırılmış girintiyle görüntüler (her seviye için 2 boşluk ekleyin)

    Departmanın görüntüleme formatı şöyle olmalıdır:

    [indent spaces][Department Name]
      [children displayed with indent + 2]
  • main.cpp: Bir organizasyon yapısı oluşturun ve görüntüleyin.

    Dört girdi okuyun:

    1. Şirket adı (string)
    2. Departman adı (string)
    3. Birinci çalışanın adı ve maaşı (format: name,salary)
    4. İkinci çalışanın adı ve maaşı (format: name,salary)

    Bu yapıyı kurun: Bir şirket (üst düzey departman) oluşturun, ona bir alt departman ekleyin ve her iki çalışanı da bu alt departmana ekleyin. Ardından tüm organizasyonu görüntüleyin ve toplam şirket maaşını yazdırın.

    Yapıyı görüntüledikten sonra şunu yazdırın:

    Total Salary: $[amount]

Örneğin, TechCorp, Engineering, Alice,75000 ve Bob,65000 girdileriyle:

[TechCorp]
  [Engineering]
    - Alice ($75000)
    - Bob ($65000)
Total Salary: $140000

StartupInc, Development, Carol,80000 ve Dave,70000 girdileriyle:

[StartupInc]
  [Development]
    - Carol ($80000)
    - Dave ($70000)
Total Salary: $150000

getSalary() metodunun bir çalışan veya bir departman üzerinde çağrılmasından bağımsız olarak nasıl tek tip çalıştığına dikkat edin; departman, tüm üyelerinin maaşlarını otomatik olarak toplar. İstemci kodunun, toplamları hesaplarken veya hiyerarşiyi görüntülerken bireysel çalışanlar ile tüm departmanlar arasında ayrım yapmasına gerek kalmaz.

Kopya kağıdı

Composite deseni, hem tekil öğelerin hem de kapsayıcıların aynı arayüzü paylaştığı ağaç yapıları oluşturarak, tekil nesnelere ve nesne gruplarına tek tip bir şekilde yaklaşır.

Desen üç temel bölümden oluşur:

  • Component (Bileşen): Ortak işlemleri tanımlayan bir arayüz
  • Leaf (Yaprak): Tekil nesneleri temsil eden sınıflar
  • Composite (Bileşik): Alt öğeleri içeren ve işlemleri onlara devreden sınıflar

Bir dosya sisteminin örnek uygulaması:

#include <iostream>
#include <memory>
#include <vector>
#include <string>

// Bileşen arayüzü
class FileSystemItem {
public:
    virtual void display(int indent = 0) const = 0;
    virtual int getSize() const = 0;
    virtual ~FileSystemItem() = default;
};

// Yaprak - tekil dosyaları temsil eder
class File : public FileSystemItem {
    std::string name;
    int size;
public:
    File(const std::string& n, int s) : name(n), size(s) {}
    
    void display(int indent = 0) const override {
        std::cout << std::string(indent, ' ') << name 
                  << " (" << size << " KB)\n";
    }
    int getSize() const override { return size; }
};

// Bileşik - diğer bileşenleri içerir
class Folder : public FileSystemItem {
    std::string name;
    std::vector<std::shared_ptr<FileSystemItem>> children;
public:
    Folder(const std::string& n) : name(n) {}
    
    void add(std::shared_ptr<FileSystemItem> item) {
        children.push_back(item);
    }
    
    void display(int indent = 0) const override {
        std::cout << std::string(indent, ' ') << "[" << name << "]\n";
        for (const auto& child : children) {
            child->display(indent + 2);
        }
    }
    
    int getSize() const override {
        int total = 0;
        for (const auto& child : children) {
            total += child->getSize();
        }
        return total;
    }
};

Bileşik (composite), alt öğeleri std::shared_ptr kullanarak saklar ve işlemleri bunlar üzerinden yineleyerek gerçekleştirir. getSize() gibi işlemler, tüm hiyerarşi boyunca toplamları hesaplayarak özyinelemeli (recursive) olarak çalışır.

Parça-bütün hiyerarşilerini temsil etmeniz gerektiğinde ve istemcilerin tekil nesnelere ve bileşimlere tek tip davranmasını istediğinizde Composite desenini kullanın.

Kendin dene

#include <iostream>
#include <string>
#include <memory>
#include <sstream>
#include "Organization.h"

int main() {
    // Girdileri oku
    std::string companyName;
    std::string departmentName;
    std::string employee1Input;
    std::string employee2Input;
    
    std::getline(std::cin, companyName);
    std::getline(std::cin, departmentName);
    std::getline(std::cin, employee1Input);
    std::getline(std::cin, employee2Input);
    
    // "name,salary" formatını ayrıştırmak için yardımcı lambda
    auto parseEmployee = [](const std::string& input) -> std::pair<std::string, int> {
        size_t commaPos = input.find(',');
        std::string name = input.substr(0, commaPos);
        int salary = std::stoi(input.substr(commaPos + 1));
        return {name, salary};
    };
    
    auto [name1, salary1] = parseEmployee(employee1Input);
    auto [name2, salary2] = parseEmployee(employee2Input);
    
    // TODO: Şirketi üst düzey bir Department olarak oluşturun
    
    // TODO: Bir alt departman oluşturun
    
    // TODO: Ayrıştırılan verileri kullanarak iki Employee nesnesi oluşturun
    
    // TODO: Çalışanları alt departmana ekleyin
    
    // TODO: Alt departmanı şirkete ekleyin
    
    // TODO: Tüm organizasyon yapısını görüntüleyin
    
    // TODO: Toplam maaşı şu formatta yazdırın: Total Salary: $[amount]
    
    return 0;
}
quiz iconKendini test et

Bu ders kısa bir quiz içerir. Soruları yanıtlamak ve ilerlemeni kaydetmek için derse başla.

Nesne Yönelimli Programlama bölümündeki tüm dersler