Menu
Coddy logo textTech

Kompozit Kalıbı

Coddy'nin Java Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası — ders 75 / 87.

Composite Deseni (Composite Pattern), nesneleri parça-bütün hiyerarşilerini temsil edecek şekilde ağaç yapılarında birleştirmenize olanak tanıyan yapısal bir tasarım desenidir. İstemcilerin, tekil nesneleri ve nesne topluluklarını ortak bir arayüz üzerinden tek tip olarak ele almalarına olanak tanır.

Bir dosya sistemini düşünün: bir klasör, dosyalar ve daha fazla dosya ve klasör içerebilen diğer klasörleri içerebilir. İster tek bir dosyayla ister tüm bir klasör yapısıyla uğraşıyor olun, "get size" gibi işlemleri aynı şekilde gerçekleştirmek istersiniz. Desen bir Component arayüzü, Leaf nesneleri (tekil öğeler) ve Composite nesneleri (alt öğeleri tutan kapsayıcılar) tanımlar:

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();
    }
}

Kompozit, işlemleri alt öğelerine devrederek özyinelemeli yapılara olanak tanır. İstemciler, bir yaprakla mı yoksa bir kompozitle mi çalıştıklarını bilmeden ağaçla etkileşime girerler:

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");

Composite Deseni; organizasyon şemaları, UI bileşenleri veya menü sistemleri gibi, gruplara ve bireylere aynı şekilde davranmanız gereken hiyerarşileri temsil etmek için idealdir.

challenge icon

Görev

Kolay

Composite Pattern kullanarak bir organizasyon şeması sistemi oluşturalım! Departmanların çalışanları ve diğer alt departmanları içerebileceği bir yapı oluşturacaksınız; bu yapı, ister tek bir çalışana ister tüm bir bölüme bakıyor olun, toplam maaşları hesaplamanıza ve hiyerarşiyi tek tip bir şekilde görüntülemenize olanak tanır.

Kodunuzu dört dosya halinde düzenleyeceksiniz:

  • OrganizationComponent.java: Hem çalışanların hem de departmanların uygulayacağı bileşen arayüzünü (interface) tanımlayın. İki yöntem bildirmelidir: bileşeni uygun girintiyle görüntülemek için showDetails(String indent) ve toplam maaşı tam sayı olarak döndüren getSalary().
  • Employee.java: Bireysel çalışanları temsil eden yaprak (leaf) sınıfını oluşturun. Bir Employee, her ikisi de yapıcı (constructor) aracılığıyla ayarlanan bir isme (String) ve maaşa (int) sahiptir. showDetails çağrıldığında, [indent][name]: $[salary] formatında çıktı vermelidir. getSalary yöntemi sadece çalışanın maaşını döndürür.
  • Department.java: Hem çalışanları hem de alt departmanları tutabilen bileşik (composite) sınıfı oluşturun. Bir Department bir isme (String) sahiptir ve OrganizationComponent çocuklarının bir listesini tutar. Üye eklemek için bir add(OrganizationComponent component) yöntemine sahip olmalıdır. showDetails çağrıldığında, [indent][name] Department yazdırır, ardından her bir çocuk üzerinde artırılmış girintiyle (iki boşluk ekleyin) showDetails yöntemini çağırır. getSalary yöntemi, tüm çocukların maaşlarının toplamını döndürür.
  • Main.java: Organizasyonunuzu kurun! Dört girdi alacaksınız: maaşlarıyla birlikte iki çalışan ismi ve bir çalışan içeren bir alt departman ismi.

    Kök dizininiz olarak bir "Engineering" departmanı oluşturun. İlk iki isim/maaş çiftini kullanarak buna iki çalışan ekleyin. Ardından üçüncü girdiyi kullanarak bir alt departman oluşturun, dördüncü isim/maaş çiftini kullanarak buna bir çalışan ekleyin ve bu alt departmanı Engineering departmanına ekleyin.

    Engineering departmanı üzerinde showDetails("") yöntemini çağırın, ardından toplam maaşı Total Salary: $[amount] formatında yazdırın.

Girdileri şu sırayla alacaksınız: employee1 name (String), employee1 salary (int), employee2 name (String), employee2 salary (int), sub-department name (String), employee3 name (String), employee3 salary (int).

Örneğin, Alice, 75000, Bob, 65000, QA, Charlie, 55000 girdileriyle çıktınız şöyle olacaktır:

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

Aynı showDetails ve getSalary yöntemlerinin, ister tek bir çalışan üzerinde ister iç içe geçmiş alt departmanlar içeren koca bir departman üzerinde çağrılsın, nasıl sorunsuz çalıştığına dikkat edin. Bileşik yapı, özyinelemeyi (recursion) otomatik olarak yöneterek tüm hiyerarşiye tek tip bir şekilde davranmanıza olanak tanır!

Kopya kağıdı

Composite Pattern (Bileşik Tasarım Deseni), nesneleri parça-bütün hiyerarşilerini temsil etmek için ağaç yapılarında birleştiren yapısal bir tasarım desenidir. Tekil nesnelerin ve bileşimlerin ortak bir arayüz üzerinden tek tip olarak ele alınmasını sağlar.

Desen üç temel öğeden oluşur:

  • Component (Bileşen): Ortak işlemleri tanımlayan arayüz
  • Leaf (Yaprak): Bileşen arayüzünü uygulayan tekil nesneler
  • Composite (Bileşik): Çocukları tutan ve işlemleri onlara devreden kapsayıcı nesneler

Bir dosya sisteminin örnek uygulaması:

// Bileşen arayüzü
interface FileComponent {
    void display(String indent);
    int getSize();
}

// Yaprak sınıfı
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;
    }
}

// Bileşik sınıfı
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();
    }
}

Bileşik yapının kullanımı:

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");

Bileşik yapı, işlemleri özyinelemeli (recursive) olarak çocuklarına devreder. İstemciler, bir yaprakla mı yoksa bir bileşikle mi çalıştıklarını bilmeden ağaçla etkileşime girerler.

Kullanım durumları: Organizasyon şemaları, kullanıcı arayüzü bileşenleri, menü sistemleri, dosya sistemleri — grupların ve bireylerin aynı muameleyi görmesi gereken her türlü hiyerarşi.

Kendin dene

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // Girdileri oku
        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: Kök olarak "Engineering" departmanını oluşturun
        
        // TODO: emp1 ve emp2 verilerini kullanarak Engineering departmanına iki çalışan oluşturun ve ekleyin
        
        // TODO: subDeptName kullanarak bir alt departman oluşturun
        
        // TODO: emp3 verilerini kullanarak alt departmana bir çalışan oluşturun ve ekleyin
        
        // TODO: Alt departmanı Engineering departmanına ekleyin
        
        // TODO: Engineering departmanı üzerinde showDetails("") metodunu çağırın
        
        // TODO: Toplam maaşı şu formatta yazdırın: Total Salary: $[amount]
    }
}
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