Structural — Yapısal Desenler
Composite
Bileşik Nesne Deseni
Amaç: Nesneleri ağaç yapısında bir araya getirmek ve istemcinin tekil nesnelere de nesne gruplarına da aynı şekilde davranabilmesini sağlamak.
Basit Anlatım
Teknik terim yok — günlük hayattan bir benzetme
Bilgisayarınızdaki klasörleri düşünün. Bir klasörün içinde dosyalar olabilir, ama başka klasörler de olabilir; onların içinde de dosyalar ve klasörler…
Bir klasörün boyutunu sorduğunuzda bilgisayar ne yapar? İçindeki her şeyin boyutunu toplar. İçerideki şey bir dosyaysa boyutunu söyler; bir klasörse, o klasör de kendi içindekileri toplar. Siz ise "dosya mı klasör mü" diye ayrım yapmadan sadece "Boyutun ne?" diye sorarsınız.
Ordudan bir örnek
Bir general "İleri!" emri verir. Emir tümenlere, tümenlerden alaylara, oradan bölüklere ve en sonunda askerlere iner. General her askere tek tek seslenmez; emir hiyerarşi boyunca kendiliğinden yayılır.
Teknik Anlamı
Bir yazılımcının bakış açısıyla
Composite, parça-bütün (part-whole) hiyerarşilerini temsil eder. Yaprak (leaf) ve kap (composite) nesneler ortak bir arayüzü uygular. Kap nesneler, işlemleri çocuklarına (children) özyinelemeli olarak delege eder.
Katılımcılar
- Component: Ortak arayüz (
IFileSystemItem). - Leaf: Çocuğu olmayan temel eleman (
FileItem). - Composite: Çocuk tutan ve işlemleri onlara delege eden eleman (
Folder). - Client: Ağaçtaki tüm elemanlarla Component arayüzü üzerinden çalışır.
Gerçek dünya kullanımları
- Dosya sistemleri, organizasyon şemaları, menü/alt menü yapıları
- UI bileşen ağaçları (WPF / WinForms kontrolleri, HTML DOM)
- Ürün paketleri: bir paket içinde ürünler ve başka paketler
- İfade ağaçları (expression trees):
(2 + 3) * 4
Avantajlar
- İstemci kodu basitleşir:
if (x is Folder)kontrollerine gerek kalmaz. - Yeni eleman türleri eklemek kolaydır (Open/Closed).
- Özyineleme (recursion) doğal biçimde ifade edilir.
Dezavantajlar
- Arayüz çok genel olabilir: bir yaprağa
Add()çağırmak anlamsızdır. Bu metotları yalnızca Composite sınıfına koymak daha güvenli bir tasarımdır.
C# ile Uygulama
Adım adım, çalışan kod örneği
Senaryo: Basit bir dosya sistemi. Toplam boyutu hesaplamak ve ağacı ekrana yazdırmak istiyoruz.
Adım 1 Component: ortak arayüz
public interface IFileSystemItem
{
string Name { get; }
long GetSize();
void Print(string indent = "");
}Adım 2 Leaf: dosya
public class FileItem : IFileSystemItem
{
private readonly long _size;
public string Name { get; }
public FileItem(string name, long size) { Name = name; _size = size; }
public long GetSize() => _size;
public void Print(string indent = "") =>
Console.WriteLine($"{indent}- {Name} ({_size} KB)");
}Adım 3 Composite: klasör
Klasör hem IFileSystemItem'dır hem de IFileSystemItem listesi tutar. GetSize() çocuklarının boyutlarını toplar; çocuk klasörse o da kendi çocuklarını toplar.
public class Folder : IFileSystemItem
{
private readonly List<IFileSystemItem> _children = new();
public string Name { get; }
public Folder(string name) => Name = name;
public Folder Add(IFileSystemItem item) { _children.Add(item); return this; }
public long GetSize() => _children.Sum(c => c.GetSize());
public void Print(string indent = "")
{
Console.WriteLine($"{indent}+ {Name}/ ({GetSize()} KB)");
foreach (var child in _children)
child.Print(indent + " ");
}
}Adım 4 Kullanım
Tüm parçaları bir araya getiren istemci (client) kodu:
var root = new Folder("Projelerim")
.Add(new FileItem("notlar.txt", 4))
.Add(new Folder("WebSitesi")
.Add(new FileItem("index.html", 12))
.Add(new FileItem("style.css", 8))
.Add(new Folder("resimler")
.Add(new FileItem("logo.png", 150))
.Add(new FileItem("banner.jpg", 420))))
.Add(new Folder("Bos klasor"));
root.Print();
// İstemci tek bir dosyayla da tüm ağaçla da aynı şekilde konuşur:
IFileSystemItem[] items = { new FileItem("tek.pdf", 300), root };
foreach (var item in items)
Console.WriteLine($"{item.Name}: toplam {item.GetSize()} KB");Konsol çıktısı
+ Projelerim/ (594 KB)
- notlar.txt (4 KB)
+ WebSitesi/ (590 KB)
- index.html (12 KB)
- style.css (8 KB)
+ resimler/ (570 KB)
- logo.png (150 KB)
- banner.jpg (420 KB)
+ Bos klasor/ (0 KB)
tek.pdf: toplam 300 KB
Projelerim: toplam 594 KBKodda dikkat edilmesi gerekenler
Folder.GetSize()içinde çocuğun dosya mı klasör mü olduğuna bakılmıyor; polimorfizm bunu bizim yerimize çözüyor.- Son döngüde tek bir dosya ile koca bir klasör ağacı aynı arayüzle kullanıldı.