C# ile Yazılım Tasarım Desenleri Rehberi (Tüm Desenler ve Örnekler)

GitHub Örnek Proje Repository: https://github.com/ayzdru/AyazDuru.Samples.SoftwareDesignPatterns

Yazılım Tasarım Desenleri Nedir?

Yazılım tasarım desenleri, yazılım geliştirme sırasında sıkça karşılaşılan problemleri çözmek için oluşturulmuş yeniden kullanılabilir çözüm şablonlarıdır.
Bunlar kodun kendisi değil, nasıl bir çözüm yolu izlenmesi gerektiğini anlatan rehberlerdir.

Yazılım Tasarım Desenleri Neden Kullanılır?

  • Tekrar kullanılabilirlik → Aynı problemi yeniden çözmezsiniz.

  • Daha anlaşılır kod → Kodun mantığı netleşir.

  • Ekip içi ortak dil → “Observer” ya da “Singleton” dendiğinde herkes ne kastedildiğini bilir.

  • Bakım kolaylığı → Kodun yönetilmesi ve geliştirilmesi kolaylaşır.

Yazılım Tasarım Desenleri

1) Oluşturucu (Creational) Yazılım Tasarım Desenleri

Nesnelerin nasıl oluşturulacağını yönetir.

  • Singleton – Uygulama boyunca bir sınıftan yalnızca tek örnek oluşturur.

  • Factory Method – Nesne oluşturma işini alt sınıflara bırakır.

  • Abstract Factory – Birbiriyle ilişkili nesne gruplarını somut sınıf belirtmeden üretir.

  • Builder – Karmaşık nesneleri adım adım inşa eder.

  • Prototype – Var olan bir nesnenin kopyasını üretir.

2) Yapısal (Structural) Yazılım Tasarım Desenleri

Sınıf ve nesnelerin birbiriyle nasıl ilişki kuracağını düzenler.

  • Adapter – Uyumsuz arayüzleri birbirine uyarlar.

  • Bridge – Soyutlama ile uygulamayı birbirinden ayırır.

  • Composite – Nesneleri ağaç yapısında hiyerarşik olarak birleştirir.

  • Decorator – Nesnelere çalışma zamanında yeni özellikler ekler.

  • Facade – Karmaşık sistemleri tek bir basit arayüzle sunar.

  • Flyweight – Çok sayıda küçük nesneyi bellek dostu şekilde yönetir.

  • Proxy – Bir nesneye erişimi kontrol eder.

3) Davranışsal (Behavioral) Yazılım Tasarım Desenleri

Nesnelerin iletişim kurma şeklini ve sorumluluk paylaşımını yönetir.

  • Chain of Responsibility – İstekleri bir işlem zincirinde sırayla iletir.

  • Command – İstekleri nesne olarak paketler.

  • Interpreter – Dil veya komut gramerini yorumlar.

  • Iterator – Koleksiyon elemanlarını sırayla dolaşır.

  • Mediator – Nesneler arasındaki iletişimi merkezi bir aracıyla yönetir.

  • Memento – Nesnelerin durumunu kaydeder ve geri yükler.

  • Observer – Bir nesnedeki değişiklikleri diğer nesnelere otomatik bildirir.

  • State – Nesnenin davranışını durumuna göre değiştirir.

  • Strategy – Algoritmaların çalışma zamanında değiştirilebilmesini sağlar.

  • Template Method – Algoritmanın iskeletini tanımlar, detayları alt sınıflara bırakır.

  • Visitor – Nesne yapısına dokunmadan yeni işlemler ekler.

Yazılım Tasarım Desenleri C# Örnek Kodlar ve Detaylı Açıklamaları

1) Oluşturucu (Creational) Yazılım Tasarım Desenleri

Singleton Tasarım Deseni

Singleton tasarım deseni, bir sınıftan yalnızca bir adet nesne oluşturulmasını ve bu nesneye uygulamanın her yerinden erişilmesini sağlar. Bu desen sayesinde, sistemin bütününde aynı veriyle çalışan tek ve tutarlı bir nesne elde edilir. Özellikle merkezi yönetim gerektiren durumlarda (örneğin, loglama mekanizması, ayar yöneticisi, veritabanı bağlantısı gibi) tercih edilir.

Avantajları:

  • Nesnenin birden fazla örneği oluşmasını engeller.
  • Global bir erişim noktası sunar.
  • Kaynak yönetimini ve tutarlılığı kolaylaştırır.

Thread-safe Singleton ise çoklu iş parçacığı ile çalışan uygulamalarda Singleton nesnesinin birden fazla kez oluşturulmasının önüne geçer. Böylece veri tutarlılığı ve sistemin beklenen davranışı korunur.

Singleton Örnek Senaryo

Bir apartmanda sadece bir adet asansör kontrol paneli vardır. Tüm apartman sakinleri asansör çağırmak için bu tek kontrol panelini kullanır. Yeni bir kontrol paneli oluşturulamaz; herkes mevcut panel üzerinden işlem yapar ve asansörün konumunu, yönünü veya kapı durumunu bu panelden öğrenir. Singleton deseni, bu kontrol panelinin sistemde tek olmasını sağlar.

Singleton C# Kod Örneği (Thread-safe)

public class ElevatorControlPanel
{
    // Kontrol panelinin tek örneği burada tutulur
    private static ElevatorControlPanel instance;

    // Kilitleme objesi, thread-safe Singleton için kullanılır
    private static readonly object lockObj = new object();

    // Yapıcı metot gizli, dışarıdan nesne oluşturulamaz
    private ElevatorControlPanel()
    {
        // Kontrol paneliyle ilgili başlatma işlemleri
    }

    // Kontrol paneline erişim sağlayan thread-safe Singleton metodu
    public static ElevatorControlPanel GetInstance()
    {
        // İlk kontrol, performans için yapılır
        if (instance == null)
        {
            // Nesne oluşturma işlemi sırasında kilitlenir
            lock (lockObj)
            {
                // İkinci kontrol, başka bir thread oluşturmuş olabilir
                if (instance == null)
                {
                    instance = new ElevatorControlPanel();
                }
            }
        }
        // Aynı nesneyi döndürür
        return instance;
    }

    // Asansör çağırma metodu
    public void CallElevator(int floor)
    {
        // Burada asansör çağırma işlemi yapılır
        Console.WriteLine($"{floor}. kata asansör çağrıldı.");
    }
}

// Kullanım örneği
class Apartment
{
    static void Main(string[] args)
    {
        // Kontrol panelinin tek örneğini alıyoruz
        ElevatorControlPanel panel1 = ElevatorControlPanel.GetInstance();
        ElevatorControlPanel panel2 = ElevatorControlPanel.GetInstance();

        // İki referans da aynı nesneyi gösterir
        panel1.CallElevator(5);

        // Burada true yazdırılır çünkü ikisi de aynı nesne
        Console.WriteLine(panel1 == panel2); // true
    }
}

Factory Method Yazılım Tasarım Deseni

Factory Method yazılım tasarım deseni, nesne oluşturma işlemini alt sınıflara bırakarak, hangi tipte nesnenin oluşturulacağına karar verme sorumluluğunu soyutlar. Yani, üst sınıflar nesne üretme sürecini yönetirken, alt sınıflar hangi tür nesnenin üretileceğini belirler.
Bu desen sayesinde kodun genişletilebilirliği artar ve yeni nesne tipleri eklenmek istendiğinde mevcut kodda değişiklik yapmak gerekmez. Factory Method; bağımlılıkları azaltır, kodun esnekliğini ve bakımını kolaylaştırır.

Avantajları:

  • Kodun esnekliğini artırır ve genişletmeyi kolaylaştırır.
  • Nesne oluşturmayı merkezi bir noktadan yönetir.
  • Yeni nesne tipleri eklerken mevcut kodun bozulmasını engeller.

Factory Method Örnek Senaryo

Bir tatlı dükkanı düşünün. Bu dükkanda pasta, kurabiye ve dondurma gibi farklı tatlılar satılıyor.
Müşteri hangi tatlıyı isterse, tatlı ustası ona uygun tatlıyı hazırlıyor. Yani, müşteri pasta isterse pasta hazırlanıyor, kurabiye isterse kurabiye hazırlanıyor.
Burada tatlı ustası bir "Factory" gibi davranır ve hangi tatlının hazırlanacağına siparişe göre karar verir. Factory Method deseni sayesinde, dükkana yeni bir tatlı türü eklemek çok kolaydır; sadece yeni bir tatlı sınıfı ve ona uygun üretici eklenir.

Factory Method C# Kod Örneği

// Tatlı arayüzü (Product Interface)
public interface IDessert
{
    void Prepare();
}

// Concrete Product - Pasta
public class Cake : IDessert
{
    public void Prepare()
    {
        // Pasta hazırlanıyor
        Console.WriteLine("Bir dilim pasta hazırlanıyor.");
    }
}

// Concrete Product - Kurabiye
public class Cookie : IDessert
{
    public void Prepare()
    {
        // Kurabiye hazırlanıyor
        Console.WriteLine("Bir adet kurabiye hazırlanıyor.");
    }
}

// Concrete Product - Dondurma
public class IceCream : IDessert
{
    public void Prepare()
    {
        // Dondurma hazırlanıyor
        Console.WriteLine("Bir top dondurma hazırlanıyor.");
    }
}

// Creator sınıfı (Factory Method içerir)
public abstract class DessertFactory
{
    // Factory Method: Hangi tatlının üretileceğine alt sınıflar karar verir
    public abstract IDessert CreateDessert();
}

// Cake Factory
public class CakeFactory : DessertFactory
{
    public override IDessert CreateDessert()
    {
        // Pasta nesnesi oluşturuluyor
        return new Cake();
    }
}

// Cookie Factory
public class CookieFactory : DessertFactory
{
    public override IDessert CreateDessert()
    {
        // Kurabiye nesnesi oluşturuluyor
        return new Cookie();
    }
}

// Ice Cream Factory
public class IceCreamFactory : DessertFactory
{
    public override IDessert CreateDessert()
    {
        // Dondurma nesnesi oluşturuluyor
        return new IceCream();
    }
}

// Kullanım örneği
class DessertShop
{
    static void Main(string[] args)
    {
        // Müşteri pasta siparişi veriyor
        DessertFactory cakeOrder = new CakeFactory();
        IDessert cake = cakeOrder.CreateDessert();
        cake.Prepare();

        // Müşteri kurabiye siparişi veriyor
        DessertFactory cookieOrder = new CookieFactory();
        IDessert cookie = cookieOrder.CreateDessert();
        cookie.Prepare();

        // Müşteri dondurma siparişi veriyor
        DessertFactory iceCreamOrder = new IceCreamFactory();
        IDessert iceCream = iceCreamOrder.CreateDessert();
        iceCream.Prepare();
    }
}

Abstract Factory Yazılım Tasarım Deseni

Abstract Factory yazılım tasarım deseni, birlikte kullanılacak birbiriyle ilişkili nesnelerin (ürün ailesinin) aynı tema, marka veya stil gibi ortak özelliklere uygun şekilde, tutarlı olarak oluşturulmasını sağlar. Ürün ailesinin her üyesi için farklı varyantlar (örneğin; Modern, Klasik, Premium) olabilir ve istemci kodu bu nesnelerin somut tiplerini bilmeden, uygun “factory” üzerinden üretim yapar.

Avantajları:

  • Bir ürün ailesinin tüm üyelerini uyumlu (aynı stile sahip) şekilde üretmeye olanak tanır.
  • Yeni bir ürün ailesi veya varyant eklemek kolaydır.
  • Kodun esnekliğini ve sürdürülebilirliğini artırır.
  • Somut sınıflara olan bağımlılığı azaltır, test edilebilirliği artırır.

Dezavantajları:

  • Sınıf sayısı ve karmaşıklığı artar.
  • Yeni ürün tipi eklemek mevcut tüm fabrikalarda güncelleme gerektirir.

Abstract Factory Örnek Senaryo

Bir otel odası dekorasyonu yapan bir şirketi düşünün. Bu şirket, müşterilerine iki farklı konsept sunuyor: Modern ve Klasik. Her oda dekorasyonunda üç ana eşya bulunuyor: sandalye, masa ve yatak.

Müşteri “Modern” konsepti seçtiğinde tüm eşyalar modern tasarıma sahip oluyor; “Klasik” seçildiğinde ise bütün eşyalar klasik tasarımlı oluyor. Böylece odada farklı stillere sahip ürünler karışmıyor ve tutarlı bir görünüm elde ediliyor.

Şirket yeni bir konsept eklemek istediğinde (örneğin “Minimalist”), sadece bu konsepte ait sandalye, masa ve yatak sınıflarını ve ilgili fabrikayı eklemesi yeterli oluyor; mevcut kodda büyük değişiklik yapmasına gerek kalmıyor.

Abstract Factory C# Kod Örneği

using System;

// -------- Abstract Product Arayüzleri --------

// Sandalye için arayüz
public interface IChair
{
    void Describe(); // Sandalyenin stilini açıklar
}

// Masa için arayüz
public interface ITable
{
    void Describe(); // Masanın stilini açıklar
}

// Yatak için arayüz
public interface IBed
{
    void Describe(); // Yatağın stilini açıklar
}

// -------- Concrete Product Sınıfları (Modern) --------

// Modern sandalye
public class ModernChair : IChair
{
    public void Describe()
    {
        // Modern sandalye tanımı
        Console.WriteLine("Modern sandalye: Metal ayaklı, sade tasarım.");
    }
}

// Modern masa
public class ModernTable : ITable
{
    public void Describe()
    {
        // Modern masa tanımı
        Console.WriteLine("Modern masa: Cam yüzeyli, minimalist çizgiler.");
    }
}

// Modern yatak
public class ModernBed : IBed
{
    public void Describe()
    {
        // Modern yatak tanımı
        Console.WriteLine("Modern yatak: Alçak, platform tipi ve düz başlık.");
    }
}

// -------- Concrete Product Sınıfları (Klasik) --------

// Klasik sandalye
public class ClassicChair : IChair
{
    public void Describe()
    {
        // Klasik sandalye tanımı
        Console.WriteLine("Klasik sandalye: Oymalı ahşap, süslü kolçaklar.");
    }
}

// Klasik masa
public class ClassicTable : ITable
{
    public void Describe()
    {
        // Klasik masa tanımı
        Console.WriteLine("Klasik masa: Kavisli ayaklar ve zengin detaylar.");
    }
}

// Klasik yatak
public class ClassicBed : IBed
{
    public void Describe()
    {
        // Klasik yatak tanımı
        Console.WriteLine("Klasik yatak: Yüksek başlık, kadife kumaş kaplama.");
    }
}

// -------- Abstract Factory Arayüzü --------

// Oda dekorasyon ürün ailesi için fabrika arayüzü
public interface IRoomFurnitureFactory
{
    IChair CreateChair();   // Sandalye oluştur
    ITable CreateTable();   // Masa oluştur
    IBed CreateBed();       // Yatak oluştur
}

// -------- Concrete Factories --------

// Modern oda fabrikası
public class ModernRoomFactory : IRoomFurnitureFactory
{
    public IChair CreateChair()
    {
        // Modern sandalye oluşturuluyor
        return new ModernChair();
    }

    public ITable CreateTable()
    {
        // Modern masa oluşturuluyor
        return new ModernTable();
    }

    public IBed CreateBed()
    {
        // Modern yatak oluşturuluyor
        return new ModernBed();
    }
}

// Klasik oda fabrikası
public class ClassicRoomFactory : IRoomFurnitureFactory
{
    public IChair CreateChair()
    {
        // Klasik sandalye oluşturuluyor
        return new ClassicChair();
    }

    public ITable CreateTable()
    {
        // Klasik masa oluşturuluyor
        return new ClassicTable();
    }

    public IBed CreateBed()
    {
        // Klasik yatak oluşturuluyor
        return new ClassicBed();
    }
}

// -------- Client (Kullanıcı) --------

// Oda dekorasyonu yapan servis
public class RoomDesigner
{
    private readonly IRoomFurnitureFactory _factory;

    public RoomDesigner(IRoomFurnitureFactory factory)
    {
        _factory = factory; // Seçilen konseptin fabrikası
    }

    public void DesignRoom()
    {
        var chair = _factory.CreateChair();
        var table = _factory.CreateTable();
        var bed = _factory.CreateBed();

        // Ürünlerin stilini açıklama
        chair.Describe();
        table.Describe();
        bed.Describe();
    }
}

// -------- Kullanım (Main) --------

public class Program
{
    public static void Main(string[] args)
    {
        // Modern oda tasarımı
        IRoomFurnitureFactory modernFactory = new ModernRoomFactory();
        var modernDesigner = new RoomDesigner(modernFactory);
        Console.WriteLine("--- Modern Oda Dekorasyonu ---");
        modernDesigner.DesignRoom();

        Console.WriteLine();

        // Klasik oda tasarımı
        IRoomFurnitureFactory classicFactory = new ClassicRoomFactory();
        var classicDesigner = new RoomDesigner(classicFactory);
        Console.WriteLine("--- Klasik Oda Dekorasyonu ---");
        classicDesigner.DesignRoom();

        // Yeni bir konsept eklemek için:
        // MinimalistChair, MinimalistTable, MinimalistBed ve MinimalistRoomFactory sınıflarını eklemeniz yeterli!
    }
}

Builder Yazılım Tasarım Deseni

Builder tasarım deseni, karmaşık nesnelerin veya aynı nesnenin farklı kombinasyonlarla adım adım ve kontrollü şekilde oluşturulmasını sağlar. Özellikle nesne oluşturma sürecinde birçok seçenek, opsiyonel veya zorunlu alan olduğunda kodun okunabilirliğini ve bakımını artırır.

Builder Örnek Senaryo

Bir tatil rezervasyon sistemi düşünün. Müşteri kendi tatil paketini oluşturabiliyor:

  • Otel seçimi
  • Oda tipi seçimi
  • Kahvaltı ve yemek seçenekleri
  • Ekstra aktiviteler (spa, şehir turu, havalimanı transferi vb.)

Her müşteri isteğine göre farklı bir tatil paketi hazırlanabiliyor. Burada Builder deseni, tatil paketinin adımlarını (otel seçimi, oda tipi, yemek ve aktiviteler) yönetiyor ve sonunda “Build” işlemiyle kişiye özel tatil paketi oluşturuluyor.

Builder C# Kod Örneği

using System;
using System.Collections.Generic;

// Tatil paketi nesnesi
public class HolidayPackage
{
    public string HotelName { get; set; }
    public string RoomType { get; set; }
    public bool HasBreakfast { get; set; }
    public List<string> Activities { get; set; }

    public void Describe()
    {
        Console.WriteLine($"Otel: {HotelName}");
        Console.WriteLine($"Oda Tipi: {RoomType}");
        Console.WriteLine($"Kahvaltı: {(HasBreakfast ? "Dahil" : "Yok")}");
        Console.WriteLine($"Aktiviteler: {(Activities.Count > 0 ? string.Join(", ", Activities) : "Yok")}");
        Console.WriteLine("----------");
    }
}

// Tatil paketi için Builder sınıfı
public class HolidayPackageBuilder
{
    private HolidayPackage _package;

    public HolidayPackageBuilder()
    {
        _package = new HolidayPackage
        {
            Activities = new List<string>()
        };
    }

    public HolidayPackageBuilder SetHotel(string hotelName)
    {
        _package.HotelName = hotelName;
        return this;
    }

    public HolidayPackageBuilder SetRoomType(string roomType)
    {
        _package.RoomType = roomType;
        return this;
    }

    public HolidayPackageBuilder AddBreakfast()
    {
        _package.HasBreakfast = true;
        return this;
    }

    public HolidayPackageBuilder AddActivity(string activity)
    {
        _package.Activities.Add(activity);
        return this;
    }

    public HolidayPackage Build()
    {
        return _package;
    }
}

// Kullanım örneği
class Program
{
    static void Main(string[] args)
    {
        // Müşteri 1: Lüks tatil paketi
        HolidayPackage luxuryPackage = new HolidayPackageBuilder()
            .SetHotel("Grand Resort")
            .SetRoomType("Suite")
            .AddBreakfast()
            .AddActivity("Spa")
            .AddActivity("Şehir Turu")
            .AddActivity("Havalimanı Transferi")
            .Build();

        Console.WriteLine("Lüks Tatil Paketi:");
        luxuryPackage.Describe();

        // Müşteri 2: Ekonomik tatil paketi
        HolidayPackage ecoPackage = new HolidayPackageBuilder()
            .SetHotel("Budget Inn")
            .SetRoomType("Standart")
            .AddActivity("Şehir Turu")
            .Build();

        Console.WriteLine("Ekonomik Tatil Paketi:");
        ecoPackage.Describe();
    }
}

Prototype Yazılım Tasarım Deseni

Prototype yazılım tasarım deseni, mevcut bir nesnenin kopyası (klonu) alınarak yeni nesneler üretmeyi sağlar. Nesne oluşturma süreci pahalı, karmaşık veya çeşitli yapılandırmalara sahip olduğunda, yeni nesneyi sıfırdan başlatmak yerine var olan bir prototip nesnenin kopyalanması tercih edilir. Bu desen sayesinde, nesneler dinamik olarak ve hızlıca türetilebilir; farklı konfigürasyonlara sahip benzer nesneler kolayca çoğaltılabilir.

Avantajları:

  • Karmaşık ve pahalı nesne oluşturma işlemlerini hızlandırır.
  • Nesnelerin çalışma zamanında dinamik olarak çoğaltılmasına imkan tanır.
  • Farklı konfigürasyonlara sahip nesnelerin üretimini kolaylaştırır.
  • Nesneye özgü özellikler (örneğin ayarlar, parametreler) korunarak kopyalanır.

Dezavantajları:

  • Derin kopya (deep copy) ve yüzeysel kopya (shallow copy) ayrımı iyi yapılmazsa beklenmedik hatalar oluşabilir.
  • Karmaşık nesnelerde kopyalama maliyeti artabilir.
  • Nesne içindeki referans türlerinde dikkatli olunmalıdır.

Prototype Örnek Senaryo

Bir çocuk oyuncak fabrikası düşünün. Farklı renkler ve özelliklerde robot oyuncaklar üretiliyor. Fabrikanın bir “prototip” robotu var. Farklı müşteriler, bu robotun üzerindeki bazı özellikleri değiştirerek (ör. renk, aksesuar) kendine özel robot istiyor. Fabrika her müşteri için sıfırdan robot üretmek yerine, prototip robotun bir kopyasını alıp istenen değişiklikleri yaparak müşteriye teslim ediyor.

Böylece üretim süreci hızlanıyor, maliyet azalıyor ve her müşteri farklı konfigürasyonda robotuna hızlıca sahip oluyor.

Prototype C# Kod Örneği

using System;

// Prototype arayüzü: Klonlama yeteneği tanımlar
public interface IRobotPrototype
{
    IRobotPrototype Clone(); // Nesnenin kopyasını alır
    void Describe();         // Robotun özelliklerini açıklar
}

// Concrete Prototype: Oyuncak robot
public class ToyRobot : IRobotPrototype
{
    public string Color { get; set; }       // Renk özelliği
    public string Accessory { get; set; }   // Aksesuar özelliği

    // Klonlama işlemi: yeni bir nesne oluşturup özellikleri kopyalar
    public IRobotPrototype Clone()
    {
        // Basit bir yüzeysel kopya (shallow copy)
        return new ToyRobot
        {
            Color = this.Color,
            Accessory = this.Accessory
        };
    }

    // Robotun özelliklerini ekrana yazdırır
    public void Describe()
    {
        Console.WriteLine($"Robot Rengi: {Color}");
        Console.WriteLine($"Robot Aksesuarı: {Accessory}");
        Console.WriteLine("-----------------------");
    }
}

// Kullanım örneği
class Program
{
    static void Main(string[] args)
    {
        // Fabrikanın prototip robotu tanımlanıyor
        ToyRobot prototypeRobot = new ToyRobot
        {
            Color = "Kırmızı",
            Accessory = "Kalkan"
        };

        Console.WriteLine("Prototip Robot:");
        prototypeRobot.Describe();

        // Müşteri 1 için robot üretiliyor (prototipten klonlanıyor, aksesuar değiştiriliyor)
        ToyRobot customerRobot1 = (ToyRobot)prototypeRobot.Clone();
        customerRobot1.Accessory = "Kılıç";
        Console.WriteLine("Müşteri 1'in Robotu:");
        customerRobot1.Describe();

        // Müşteri 2 için robot üretiliyor (prototipten klonlanıyor, renk değiştiriliyor)
        ToyRobot customerRobot2 = (ToyRobot)prototypeRobot.Clone();
        customerRobot2.Color = "Mavi";
        Console.WriteLine("Müşteri 2'nin Robotu:");
        customerRobot2.Describe();

        // Müşteri 3 için tamamen farklı özelliklerle robot
        ToyRobot customerRobot3 = (ToyRobot)prototypeRobot.Clone();
        customerRobot3.Color = "Yeşil";
        customerRobot3.Accessory = "Jetpack";
        Console.WriteLine("Müşteri 3'ün Robotu:");
        customerRobot3.Describe();
    }
}

2) Yapısal (Structural) Yazılım Tasarım Desenleri

Adapter Yazılım Tasarım Deseni

Adapter (Uyarlayıcı) yazılım tasarım deseni, birbirleriyle uyumsuz olan iki farklı arayüzü olan bileşenin birlikte çalışmasını sağlamak için kullanılır. Eski sistemler ile yeni teknolojiler, farklı üreticilerin cihazları veya farklı protokoller bazen doğrudan bir arada çalışamaz. Adapter deseni, bu uyumsuzluğu ortadan kaldırarak mevcut bir sınıfın arayüzünü, istemcinin beklediği başka bir arayüze dönüştürür.
Adapter; kodun yeniden kullanılabilirliğini artırır, eski kodu veya harici kütüphaneyi değiştirmeden sisteme entegre edebilmenize olanak tanır.

Avantajları:

  • Farklı sistemlerin uyumlu çalışmasını sağlar.
  • Kodun esnekliğini ve tekrar kullanılabilirliğini artırır.
  • Mevcut kod ya da harici bileşenlerin değiştirilmesine gerek kalmaz.

Dezavantajları:

  • Sisteme ek bir katman ekler ve karmaşıklığı artırabilir.
  • Çok fazla adapter kullanımı kodun bakımını zorlaştırabilir.

Adapter Örnek Senaryo

Bir otelde farklı ülkelerden gelen misafirler, kendi ülkelerine özgü elektrik fişleriyle (örneğin İngiltere tipi üç uçlu priz, ABD tipi iki düz uçlu priz, Avrupa tipi iki yuvarlak uçlu priz) cihazlarını şarj etmek istiyor.
Otel odalarında ise sadece Avrupa tipi prizler var. Misafirler kendi cihazlarını doğrudan otel prizine takamazlar.
Burada “Priz Adaptörü” devreye girer. Misafir, cihazını priz adaptörüne bağlar, adaptör ise otel prizine uyumlu hale getirir.
Böylece misafirler farklı tipteki cihazlarını sorunsuzca şarj edebilirler.
Adapter nesnesi, cihazın fiş tipini otel prizinin arayüzüne çevirir ve iki farklı teknolojinin uyumlu şekilde çalışmasını sağlar.

Adapter C# Kod Örneği

using System;

// Otel prizinin arayüzü (Avrupa tipi priz)
public interface IEuropeanSocket
{
    void PowerWithEuropeanPlug(string deviceName); // Sadece Avrupa tipi fiş uyumlu cihazları çalıştırır
}

// Avrupa tipi priz (Otel prizi)
public class EuropeanSocket : IEuropeanSocket
{
    public void PowerWithEuropeanPlug(string deviceName)
    {
        Console.WriteLine("Otel prizinde çalışan cihaz: " + deviceName);
    }
}

// İngiltere tipi fişli cihaz arayüzü
public interface IUKPlugDevice
{
    string ConnectWithUKPlug(); // İngiltere fişiyle bağlanır
}

// İngiltere'den gelen cihaz (ör: kettle)
public class UKKettle : IUKPlugDevice
{
    public string ConnectWithUKPlug()
    {
        return "İngiliz Kettle (UK plug)";
    }
}

// Adapter: İngiltere tipi fişi Avrupa prizine çevirir
public class UKToEuropeanAdapter : IEuropeanSocket
{
    private readonly IUKPlugDevice _ukDevice;

    public UKToEuropeanAdapter(IUKPlugDevice ukDevice)
    {
        _ukDevice = ukDevice;
    }

    public void PowerWithEuropeanPlug(string deviceName)
    {
        string ukDeviceName = _ukDevice.ConnectWithUKPlug();
        string europeanDeviceName = ConvertUKPlugToEuropean(ukDeviceName);
        Console.WriteLine("Adapter ile otel prizinde çalışan cihaz: " + europeanDeviceName);
    }

    // Basit dönüşüm örneği
    private string ConvertUKPlugToEuropean(string ukName)
    {
        return ukName.Replace("UK plug", "European plug");
    }
}

// Kullanım örneği
class Program
{
    static void Main(string[] args)
    {
        // Doğrudan Avrupa prizine takılan cihaz
        IEuropeanSocket europeanSocket = new EuropeanSocket();
        europeanSocket.PowerWithEuropeanPlug("Saç Kurutma Makinesi (European plug)");

        Console.WriteLine();

        // İngiltere fişli kettle adapter ile otel prizine bağlanıyor
        IUKPlugDevice ukKettle = new UKKettle();
        IEuropeanSocket adapter = new UKToEuropeanAdapter(ukKettle);
        adapter.PowerWithEuropeanPlug(""); // Arayüz gereği parametre boş geçildi
    }
}

Bridge Yazılım Tasarım Deseni

Bridge (Köprü) yazılım tasarım deseni, bir nesnenin soyutlaması (abstraction) ile onun implementasyonunu (gerçekleştirimini) birbirinden ayırarak bağımsız olarak gelişmelerine olanak tanır. Yani, bir sınıfın hem davranışını hem de uygulamasını bağımsızca genişletebiliriz. Bu desen sayesinde, çok sayıda farklı kombinasyonun olduğu senaryolarda kod karmaşıklığı azalır ve esneklik artar.

Bridge deseni iki ana parçadan oluşur:

  • Abstraction (Soyutlama): Kullanıcıya sunulan ana arayüz veya sınıf.
  • Implementation (Gerçekleştirim): Soyutlamanın nasıl çalışacağını belirleyen detaylar.

Bridge ile, yeni bir soyutlama veya yeni bir gerçekleştirim eklemek istediğinizde mevcut kodu değiştirmek zorunda kalmazsınız.

Avantajları:

  • Soyutlama ve gerçekleştirim birbirinden bağımsızca geliştirilebilir.
  • Kodda değişiklik ve genişletme daha kolaydır.
  • Çoklu kombinasyonlarda kod tekrarını ve karmaşıklığı azaltır.

Dezavantajları:

  • Yapısı ilk bakışta karmaşık görünebilir.
  • Tasarım aşamasında iyi planlama gerektirir.

Bridge Örnek Senaryo

Bir kargo şirketinde çeşitli taşıma araçları (Kamyon, Gemi, Uçak) ve farklı yük türleri (Kırılabilir, Sıvı, Standart) bulunmaktadır.
Şirket, yük tipine göre uygun aracı seçip, taşıma işlemini güvenli şekilde gerçekleştirmek istiyor. Yük türleri ve araç tipleri zamanla değişebilir ve yeni türler eklenebilir.
Bridge deseni burada devreye girer: Araç tipleri ve yük türleri ayrı ayrı geliştirilir ve istenen kombinasyonlar kolayca birleştirilebilir.
Böylece sisteme yeni bir araç veya yük türü eklemek için mevcut kodu değiştirmeye gerek kalmaz; sadece yeni sınıflar eklenir ve köprülenir.

Bridge C# Kod Örneği

using System;

// Implementor arayüzü: Taşıma aracı
public interface ITransportVehicle
{
    void Carry(string cargoType); // Belirli yük tipini taşır
}

// Concrete Implementor: Kamyon
public class Truck : ITransportVehicle
{
    public void Carry(string cargoType)
    {
        Console.WriteLine($"{cargoType} yükü kamyon ile taşınıyor.");
    }
}

// Concrete Implementor: Gemi
public class Ship : ITransportVehicle
{
    public void Carry(string cargoType)
    {
        Console.WriteLine($"{cargoType} yükü gemi ile taşınıyor.");
    }
}

// Concrete Implementor: Uçak
public class Airplane : ITransportVehicle
{
    public void Carry(string cargoType)
    {
        Console.WriteLine($"{cargoType} yükü uçak ile taşınıyor.");
    }
}

// Abstraction: Yük (kargo) sınıfı
public abstract class Cargo
{
    protected ITransportVehicle transportVehicle;

    public Cargo(ITransportVehicle vehicle)
    {
        transportVehicle = vehicle;
    }

    public abstract void Transport(); // Yükün taşınmasını başlatır
}

// Refined Abstraction: Kırılabilir yük
public class FragileCargo : Cargo
{
    public FragileCargo(ITransportVehicle vehicle) : base(vehicle) { }

    public override void Transport()
    {
        Console.WriteLine("Kırılabilir yük dikkatli şekilde taşınıyor:");
        transportVehicle.Carry("Kırılabilir");
    }
}

// Refined Abstraction: Sıvı yük
public class LiquidCargo : Cargo
{
    public LiquidCargo(ITransportVehicle vehicle) : base(vehicle) { }

    public override void Transport()
    {
        Console.WriteLine("Sıvı yük özel koşullarda taşınıyor:");
        transportVehicle.Carry("Sıvı");
    }
}

// Refined Abstraction: Standart yük
public class StandardCargo : Cargo
{
    public StandardCargo(ITransportVehicle vehicle) : base(vehicle) { }

    public override void Transport()
    {
        Console.WriteLine("Standart yük taşınıyor:");
        transportVehicle.Carry("Standart");
    }
}

// Kullanım örneği
class Program
{
    static void Main(string[] args)
    {
        // Kırılabilir yük kamyon ile taşınıyor
        Cargo fragileByTruck = new FragileCargo(new Truck());
        fragileByTruck.Transport();

        Console.WriteLine();

        // Sıvı yük gemi ile taşınıyor
        Cargo liquidByShip = new LiquidCargo(new Ship());
        liquidByShip.Transport();

        Console.WriteLine();

        // Standart yük uçak ile taşınıyor
        Cargo standardByAirplane = new StandardCargo(new Airplane());
        standardByAirplane.Transport();

        Console.WriteLine();

        // Kırılabilir yük uçak ile taşınıyor
        Cargo fragileByAirplane = new FragileCargo(new Airplane());
        fragileByAirplane.Transport();
    }
}

Composite Yazılım Tasarım Deseni

Composite (Bileşik) yazılım tasarım deseni, nesne hiyerarşilerinde hem tekil nesnelerle hem de bu nesnelerin gruplarıyla aynı şekilde işlem yapabilmemizi sağlar. Yani, bir nesne ile bir nesne grubunu (örneğin bir dosya ile bir klasörü) aynı arayüz üzerinden yönetmek mümkündür.
Bu desen, özellikle ağaç yapısındaki veri modellerinin (örneğin dosya sistemleri, organizasyon şemaları, menü yapıları) uygulanmasında büyük kolaylık sağlar.

Bileşenler ikiye ayrılır:

  • Leaf (Yaprak): Alt elemanı olmayan temel nesne (örneğin bir dosya).
  • Composite (Bileşik): Diğer bileşenleri (yaprakları ve/veya başka bileşikleri) içinde barındırabilen grup nesnesi (örneğin bir klasör).

Tekil nesne ile grup nesnesi arasında işlem yaparken arayüz farkı olmaksızın aynı metotlar çağrılır; böylece kod sadeleşir ve genişletilebilirlik artar.

Avantajları:

  • Tekil ve grup nesnelerle aynı arayüz üzerinden işlem yapılabilir.
  • Ağaç yapısındaki veri modellerinde kod tekrarını önler.
  • Yeni nesne türlerini eklemek kolaydır.

Dezavantajları:

  • İleri düzeyde karmaşık ağaç yapılarında takibi zorlaşabilir.
  • Kökten uca kontrol bazen zor olabilir.

Composite Örnek Senaryo

Bir restoranda dijital menü sistemi var. Menüde tek tek yemekler (örneğin “Pizza”, “Salata”) olduğu gibi, “Menüler” adı altında birden fazla yemeği birleştiren grup seçenekleri de bulunuyor (örneğin “Akşam Menüsü” içinde “Pizza” ve “Salata” var).
Restoran yönetimi, hem tek bir yemeği, hem de bir menüdeki tüm yemekleri aynı şekilde ekleyebilmeli, fiyatlarını topluca görebilmeli ve menüye yeni yemek veya menü ekleyebilmelidir.
Composite deseni burada devreye girer: Tek yemekler “Yaprak” nesne, menüler ise “Bileşik” nesnedir. Hem yemek hem menü aynı arayüzle yönetilir; menüye yeni bir yemek veya menü eklenmek istenirse kodda değişiklik gerekmez.

Composite C# Kod Örneği

using System;
using System.Collections.Generic;

// Ortak arayüz: Menü öğesi (yemek veya menü grubu olabilir)
public interface IMenuComponent
{
    void Display(); // Menü öğesini ekrana yazdır
    decimal GetPrice(); // Menü öğesinin fiyatını döndür
}

// Leaf (Yaprak): Tek yemek
public class FoodItem : IMenuComponent
{
    public string Name { get; set; }
    public decimal Price { get; set; }

    public FoodItem(string name, decimal price)
    {
        Name = name;
        Price = price;
    }

    public void Display()
    {
        Console.WriteLine($"- {Name} : {Price} TL");
    }

    public decimal GetPrice()
    {
        return Price;
    }
}

// Composite (Bileşik): Menü grubu
public class MenuGroup : IMenuComponent
{
    public string GroupName { get; set; }
    private List<IMenuComponent> _items = new List<IMenuComponent>();

    public MenuGroup(string groupName)
    {
        GroupName = groupName;
    }

    // Menüye yeni yemek veya menü eklenebilir
    public void Add(IMenuComponent item)
    {
        _items.Add(item);
    }

    public void Remove(IMenuComponent item)
    {
        _items.Remove(item);
    }

    public void Display()
    {
        Console.WriteLine($"[{GroupName}]");
        foreach (var item in _items)
        {
            item.Display();
        }
    }

    public decimal GetPrice()
    {
        decimal total = 0;
        foreach (var item in _items)
        {
            total += item.GetPrice();
        }
        return total;
    }
}

// Kullanım örneği
class Program
{
    static void Main(string[] args)
    {
        // Tek yemekler
        FoodItem pizza = new FoodItem("Pizza", 120);
        FoodItem salad = new FoodItem("Salata", 60);
        FoodItem dessert = new FoodItem("Tatlı", 40);

        // Menü grubu (Akşam Menüsü)
        MenuGroup dinnerMenu = new MenuGroup("Akşam Menüsü");
        dinnerMenu.Add(pizza);
        dinnerMenu.Add(salad);

        // Menü grubu (Tatlı Menüsü)
        MenuGroup dessertMenu = new MenuGroup("Tatlı Menüsü");
        dessertMenu.Add(dessert);

        // Ana menü (Restoran Menüsü)
        MenuGroup restaurantMenu = new MenuGroup("Restoran Menüsü");
        restaurantMenu.Add(dinnerMenu);
        restaurantMenu.Add(dessertMenu);

        // Menüye yeni bir yemek ekleniyor
        FoodItem soup = new FoodItem("Çorba", 30);
        restaurantMenu.Add(soup);

        // Menü görüntüleniyor ve toplam fiyat hesaplanıyor
        restaurantMenu.Display();
        Console.WriteLine($"Toplam Menü Fiyatı: {restaurantMenu.GetPrice()} TL");
    }
}

Decorator Yazılım Tasarım Deseni

Decorator (Süsleyici) yazılım tasarım deseni, bir nesnenin davranışını çalışma zamanında dinamik olarak ek özelliklerle genişletmek için kullanılır. Bu desen sayesinde, nesnenin temel işlevselliğini değiştirmeden yeni yetenekler ekleyebiliriz. Kalıtım yerine nesneye dışarıdan bileşenler ekleyerek esnek ve sürdürülebilir bir yapı elde ederiz. Decorator’ler birbirine zincirlenerek bir nesneye birden fazla özellik eklenebilir ve farklı kombinasyonlar kolayca oluşturulabilir.

Avantajları:

  • Nesnenin davranışı runtime’da dinamik olarak değiştirilebilir.
  • Kod tekrarını azaltır, yeni özellik eklemek için temel sınıfı değiştirmek gerekmez.
  • Farklı decorator’ler ile zengin kombinasyonlar oluşturulabilir.

Dezavantajları:

  • Çok fazla dekoratör kullanımı kodun okunabilirliğini zorlaştırabilir.
  • Nesne akışını takip etmek karmaşık hale gelebilir.

Decorator Örnek Senaryo

Bir pastanede müşteriler pasta siparişi veriyor. Temel bir "Kek" sipariş ettiklerinde üzerine çeşitli süsler (Çikolata, Meyve, Şekerleme) eklenebiliyor. Her eklenen süs hem pastanın görünümünü hem de fiyatını değiştiriyor.
Müşteri isterse sade kek alabiliyor, isterse üzerine bir veya birden fazla süs ekletebiliyor.
Burada Decorator deseni devreye girer: Sade kek nesnesine, çikolata veya meyve gibi dekoratörler eklenerek her seferinde farklı bir pasta elde edilir. Bu kombinasyonlar kodda yeni pasta sınıfı yazmadan kolayca oluşturulabilir.

Decorator C# Kod Örneği

using System;

// Ana arayüz: Pasta
public interface ICake
{
    string GetDescription(); // Pastanın açıklaması
    double GetCost();        // Pastanın fiyatı
}

// Temel pasta: Sade kek
public class PlainCake : ICake
{
    public string GetDescription()
    {
        return "Sade Kek";
    }

    public double GetCost()
    {
        return 70.0;
    }
}

// Decorator temel sınıfı
public abstract class CakeDecorator : ICake
{
    protected ICake _cake;

    public CakeDecorator(ICake cake)
    {
        _cake = cake;
    }

    public virtual string GetDescription()
    {
        return _cake.GetDescription();
    }

    public virtual double GetCost()
    {
        return _cake.GetCost();
    }
}

// Çikolata ekleyen dekoratör
public class ChocolateDecorator : CakeDecorator
{
    public ChocolateDecorator(ICake cake) : base(cake) { }

    public override string GetDescription()
    {
        return _cake.GetDescription() + ", Çikolata";
    }

    public override double GetCost()
    {
        return _cake.GetCost() + 18.0;
    }
}

// Meyve ekleyen dekoratör
public class FruitDecorator : CakeDecorator
{
    public FruitDecorator(ICake cake) : base(cake) { }

    public override string GetDescription()
    {
        return _cake.GetDescription() + ", Meyve";
    }

    public override double GetCost()
    {
        return _cake.GetCost() + 15.0;
    }
}

// Şekerleme ekleyen dekoratör
public class CandyDecorator : CakeDecorator
{
    public CandyDecorator(ICake cake) : base(cake) { }

    public override string GetDescription()
    {
        return _cake.GetDescription() + ", Şekerleme";
    }

    public override double GetCost()
    {
        return _cake.GetCost() + 8.0;
    }
}

// Kullanım örneği
class Program
{
    static void Main(string[] args)
    {
        // Sade kek
        ICake plainCake = new PlainCake();
        Console.WriteLine($"{plainCake.GetDescription()} - {plainCake.GetCost()} TL");

        // Çikolatalı kek
        ICake chocolateCake = new ChocolateDecorator(new PlainCake());
        Console.WriteLine($"{chocolateCake.GetDescription()} - {chocolateCake.GetCost()} TL");

        // Meyveli, çikolatalı kek
        ICake fruitChocolateCake = new FruitDecorator(new ChocolateDecorator(new PlainCake()));
        Console.WriteLine($"{fruitChocolateCake.GetDescription()} - {fruitChocolateCake.GetCost()} TL");

        // Meyveli, şekerlemeli, çikolatalı kek
        ICake fullCake = new CandyDecorator(new FruitDecorator(new ChocolateDecorator(new PlainCake())));
        Console.WriteLine($"{fullCake.GetDescription()} - {fullCake.GetCost()} TL");
    }
}

Facade Yazılım Tasarım Deseni

Facade (Cephe) yazılım tasarım deseni, bir sistemin karmaşık alt bileşenlerini basit bir arayüz (cephe) ile dış dünyaya sunar. Yani Facade, sistemin içindeki birçok nesne ve metodu kolayca kullanabilmek için tek bir giriş noktası oluşturur. Bu sayede hem kodun okunabilirliği artar hem de sistemin karmaşası dışarıdan gizlenmiş olur.
Facade, özellikle büyük ve karmaşık alt sistemlerin yönetilmesi gerektiğinde tercih edilir. Yeni başlayanlar veya sistemi dışarıdan kullananlar için işlemleri kolaylaştırır ve hata riskini azaltır.

Avantajları:

  • Kullanıcıya sade ve anlaşılır bir arayüz sunar.
  • Alt sistemlerin karmaşıklığını gizler.
  • Kodun bakımını ve genişletilmesini kolaylaştırır.
  • Sistemler arası entegrasyonu kolaylaştırır.

Dezavantajları:

  • Fazla soyutlama gereksiz karmaşıklık yaratabilir.
  • Alt sistemlerin tüm işlevlerini her zaman kapsamayabilir.

Facade Örnek Senaryo

Bir bilgisayarı açmak, aslında birden fazla alt sistemi başlatmak anlamına gelir:

  • Güç yönetimi sistemi bilgisayara elektrik sağlar
  • İşletim sistemi yüklenir
  • Ekran açılır
  • Klavye ve fare etkinleştirilir
    Normalde her bir sistemi ayrı ayrı başlatmak gerekirdi. Facade deseni sayesinde, kullanıcı sadece "Bilgisayarı Aç" tuşuna basar ve arka planda tüm bu işlemler facade nesnesi tarafından otomatik olarak başlatılır.
    Bu hem kullanıcının işini kolaylaştırır hem de bilgisayarda hatasız ve güvenli bir açılış süreci sağlar.

Facade C# Kod Örneği

using System;

// Alt sistem: Güç yönetimi
public class PowerManager
{
    public void PowerOn()
    {
        Console.WriteLine("Güç yönetimi: Bilgisayara elektrik verildi.");
    }

    public void PowerOff()
    {
        Console.WriteLine("Güç yönetimi: Bilgisayar kapatıldı.");
    }
}

// Alt sistem: İşletim sistemi yükleyici
public class OperatingSystemLoader
{
    public void LoadOS()
    {
        Console.WriteLine("İşletim sistemi yüklendi.");
    }

    public void ShutdownOS()
    {
        Console.WriteLine("İşletim sistemi kapatıldı.");
    }
}

// Alt sistem: Ekran kontrolü
public class MonitorController
{
    public void TurnOnMonitor()
    {
        Console.WriteLine("Ekran açıldı.");
    }

    public void TurnOffMonitor()
    {
        Console.WriteLine("Ekran kapatıldı.");
    }
}

// Alt sistem: Klavye ve fare kontrolü
public class PeripheralController
{
    public void EnablePeripherals()
    {
        Console.WriteLine("Klavye ve fare etkinleştirildi.");
    }

    public void DisablePeripherals()
    {
        Console.WriteLine("Klavye ve fare devre dışı bırakıldı.");
    }
}

// Facade: Bilgisayar açma/kapama işlemlerini yöneten cephe
public class ComputerFacade
{
    private PowerManager _power;
    private OperatingSystemLoader _os;
    private MonitorController _monitor;
    private PeripheralController _peripherals;

    public ComputerFacade()
    {
        _power = new PowerManager();
        _os = new OperatingSystemLoader();
        _monitor = new MonitorController();
        _peripherals = new PeripheralController();
    }

    // Bilgisayarı açma işlemi
    public void StartComputer()
    {
        Console.WriteLine("Bilgisayar açılıyor...");
        _power.PowerOn();
        _monitor.TurnOnMonitor();
        _os.LoadOS();
        _peripherals.EnablePeripherals();
        Console.WriteLine("Bilgisayar açıldı ve kullanıma hazır!");
    }

    // Bilgisayarı kapatma işlemi
    public void ShutdownComputer()
    {
        Console.WriteLine("Bilgisayar kapatılıyor...");
        _peripherals.DisablePeripherals();
        _os.ShutdownOS();
        _monitor.TurnOffMonitor();
        _power.PowerOff();
        Console.WriteLine("Bilgisayar güvenli şekilde kapatıldı!");
    }
}

// Kullanım örneği
class Program
{
    static void Main(string[] args)
    {
        ComputerFacade computer = new ComputerFacade();

        // Bilgisayarı açma
        computer.StartComputer();

        Console.WriteLine();

        // Bilgisayarı kapatma
        computer.ShutdownComputer();
    }
}

Flyweight Yazılım Tasarım Deseni

Flyweight (Hafif Sıklet) yazılım tasarım deseni, çok sayıda benzer nesne oluşturulması gereken durumlarda bellek ve kaynak kullanımını optimize etmek için ortak verileri paylaşmayı hedefler. Bu desen, nesnelerin ortak olan (intrinsic) ve nesneye/olaya özel olan (extrinsic) kısımlarını ayırır. Böylece, büyük miktarda tekrar eden nesnelerin ortak özellikleri hafızada yalnızca bir kez tutulur, nesneye özel bilgiler ise dışarıdan iletilir.

Flyweight deseni; grafik editörleri, oyunlar, metin işleyiciler gibi binlerce benzer nesneye sahip uygulamalarda sıkça tercih edilir. Bu sayede hem performans artar hem de bellekten ciddi şekilde tasarruf edilir.

Flyweight Örnek Senaryo

Bir bilgisayar oyununda şehir haritası üzerinde binlerce ev bulunuyor.
Her evin tipi (apartman, villa, müstakil ev gibi) ve dış görünüşü (grafik dosyası) aynı olabilir, fakat her birinin konumu, adresi ve pencere sayısı gibi özellikleri farklıdır.
Eğer her ev için ayrı ayrı grafik ve tip bilgisi tutulursa, bellek gereksiz yere şişer.
Flyweight deseni sayesinde, tüm evler ortak bir “ev tipi” nesnesini paylaşır; her evin kendine özel konumu, adresi ve pencere sayısı gibi bilgiler ise ayrı tutulur.
Böylece, şehir haritasında binlerce ev olsa bile, bellekte sadece birkaç ev tipi nesnesi vardır ve büyük bir tasarruf sağlanır.

 

Flyweight C# Kod Örneği

using System;
using System.Collections.Generic;

// Flyweight: Ortak ev tipi nesnesi
public class HouseTypeFlyweight
{
    public string TypeName { get; private set; }      // Ev tipi (Apartman, Villa, Müstakil Ev)
    public string TextureFile { get; private set; }   // Ortak grafik dosyası

    public HouseTypeFlyweight(string typeName, string textureFile)
    {
        TypeName = typeName;
        TextureFile = textureFile;
    }

    // Ortak ev tipiyle evi haritada çiz
    public void Draw(int x, int y, string address, int windowCount)
    {
        Console.WriteLine($"{TypeName} ({TextureFile}) {x},{y} konumunda, adres: {address}, pencere sayısı: {windowCount} ile çizildi.");
    }
}

// Flyweight Factory: Aynı tip için tek nesne tutar
public class HouseTypeFlyweightFactory
{
    private Dictionary<string, HouseTypeFlyweight> _houseTypes = new Dictionary<string, HouseTypeFlyweight>();

    public HouseTypeFlyweight GetHouseType(string typeName, string textureFile)
    {
        string key = typeName + "_" + textureFile;
        if (!_houseTypes.ContainsKey(key))
        {
            _houseTypes[key] = new HouseTypeFlyweight(typeName, textureFile);
        }
        return _houseTypes[key];
    }
}

// Şehir haritasındaki tekil ev nesnesi (extrinsic veri: konum, adres, pencere sayısı; intrinsic veri: ev tipi)
public class CityHouse
{
    public int X { get; set; }
    public int Y { get; set; }
    public string Address { get; set; }
    public int WindowCount { get; set; }
    private HouseTypeFlyweight _houseType;

    public CityHouse(int x, int y, string address, int windowCount, HouseTypeFlyweight houseType)
    {
        X = x;
        Y = y;
        Address = address;
        WindowCount = windowCount;
        _houseType = houseType;
    }

    // Evi haritada çiz
    public void DrawHouse()
    {
        _houseType.Draw(X, Y, Address, WindowCount);
    }
}

// Kullanım örneği
class Program
{
    static void Main(string[] args)
    {
        HouseTypeFlyweightFactory factory = new HouseTypeFlyweightFactory();

        // Farklı ev tipleri için tek nesne oluşturuluyor
        HouseTypeFlyweight apartmentType = factory.GetHouseType("Apartman", "apartment.png");
        HouseTypeFlyweight villaType = factory.GetHouseType("Villa", "villa.png");

        // Şehir haritasında çeşitli evler oluşturuluyor
        var cityHouses = new List<CityHouse>
        {
            new CityHouse(10, 20, "Atatürk Cd. No:5", 12, apartmentType),
            new CityHouse(30, 60, "Cumhuriyet Mh. No:18", 8, villaType),
            new CityHouse(50, 90, "Barış Sok. No:33", 14, apartmentType),
            new CityHouse(70, 120, "Güneş Cd. No:2", 6, villaType),
        };

        // Tüm evler haritada çiziliyor
        foreach (var house in cityHouses)
        {
            house.DrawHouse();
        }

        // Bellekte, apartment.png ve villa.png için sadece iki ev tipi nesnesi var!
    }
}

Proxy Yazılım Tasarım Deseni

Proxy (Vekil) yazılım tasarım deseni, bir nesneye erişim sağlanırken araya bir vekil (proxy) nesnesi koyarak erişimi yönetmenizi, kontrol etmenizi veya ek sorumluluklar eklemenizi sağlar. Proxy nesnesi, istemci ile gerçek nesne arasındaki iletişimi sağlar ve genellikle erişim kontrolü, loglama, önbellekleme, güvenlik veya uzaktaki nesnelere erişim gibi işlemler için kullanılır.
Bu desen; pahalı nesnelerin gecikmeli başlatılması, uzak servis çağrıları, yetki denetimi veya kaynak yönetimi gibi senaryolarda sıkça kullanılır. Proxy sayesinde sistem daha güvenli ve yönetilebilir hale gelir.

Avantajları:

  • Nesneye erişimi ve işlemleri kontrol altına alır.
  • Güvenlik, loglama, önbellekleme gibi ek işlemler kolayca eklenebilir.
  • Gerçek nesnenin oluşturulmasını geciktirebilir.
  • Ağ veya sistem üzerinde pahalı nesnelerin yönetimini kolaylaştırır.

Dezavantajları:

  • Ekstra kod karmaşıklığı getirebilir.
  • Proxy katmanındaki kontroller performansı bir miktar etkileyebilir.

Proxy Örnek Senaryo

Bir ofisteki güvenli bir oda (örneğin kasa odası) düşünün.
Çalışanlar bu odaya girmek istediklerinde doğrudan kapıyı açamazlar.
Önce bir güvenlik görevlisi (proxy) ile iletişim kurarlar.
Güvenlik görevlisi, kişinin yetkili olup olmadığını kontrol eder, giriş saatinin uygunluğunu denetler ve kaydı alır.
Yalnızca tüm şartlar sağlandığında çalışan gerçek kasa odasına girebilir.
Bu sayede hem güvenlik hem de giriş-çıkışların takibi sağlanır.

Proxy C# Kod Örneği

using System;

// Gerçek kasa odası nesnesi
public class RealSafeRoom : ISafeRoom
{
    public void Enter(string employeeName)
    {
        // Gerçek oda erişimi
        Console.WriteLine($"{employeeName} kasa odasına girdi.");
    }
}

// Proxy arayüzü
public interface ISafeRoom
{
    void Enter(string employeeName);
}

// Proxy nesnesi: Oda erişimi ve kontrol
public class SafeRoomProxy : ISafeRoom
{
    private RealSafeRoom _realSafeRoom;
    private string _employeeRole;
    private DateTime _currentTime;

    public SafeRoomProxy(string employeeRole, DateTime currentTime)
    {
        _realSafeRoom = new RealSafeRoom();
        _employeeRole = employeeRole;
        _currentTime = currentTime;
    }

    // Proxy üzerinden oda giriş işlemi
    public void Enter(string employeeName)
    {
        // Yetki kontrolü
        if (_employeeRole != "manager" && _employeeRole != "accountant")
        {
            Console.WriteLine("Yetkisiz giriş: Sadece yönetici veya muhasebeci girebilir!");
            return;
        }

        // Giriş saat kontrolü (örnek: 09:00 - 18:00 arası)
        if (_currentTime.Hour < 9 || _currentTime.Hour > 18)
        {
            Console.WriteLine("Kasa odasına sadece mesai saatlerinde giriş yapılabilir!");
            return;
        }

        // Loglama işlemi
        Console.WriteLine($"Log: {_employeeRole} rolündeki {employeeName} kasa odasına giriş yapıyor. Saat: {_currentTime}");

        // Gerçek oda erişimi
        _realSafeRoom.Enter(employeeName);
    }
}

// Kullanım örneği
class Program
{
    static void Main(string[] args)
    {
        // Yetkili ve uygun saatte bir yönetici
        ISafeRoom proxy1 = new SafeRoomProxy("manager", new DateTime(2025, 8, 10, 10, 30, 0));
        proxy1.Enter("Ahmet Yılmaz");

        Console.WriteLine();

        // Yetkisiz çalışan
        ISafeRoom proxy2 = new SafeRoomProxy("staff", new DateTime(2025, 8, 10, 10, 30, 0));
        proxy2.Enter("Mehmet Kaya");

        Console.WriteLine();

        // Mesai saati dışında giriş denemesi
        ISafeRoom proxy3 = new SafeRoomProxy("accountant", new DateTime(2025, 8, 10, 19, 00, 0));
        proxy3.Enter("Ayşe Demir");
    }
}

3) Davranışsal (Behavioral) Yazılım Tasarım Desenleri

Chain of Responsibility Yazılım Tasarım Deseni

Chain of Responsibility (Sorumluluk Zinciri) yazılım tasarım deseni, bir isteğin işlenmesi için bir dizi nesne oluşturur ve bu nesneleri birbirine zincirler. Her nesne, kendisine gelen isteği işleyip işleyemeyeceğine karar verir; eğer işleyemiyorsa isteği zincirdeki bir sonraki nesneye iletir. Bu sayede, istemci hangi nesnenin isteği işleyeceğini bilmek zorunda kalmaz ve zincire yeni işlemci eklemek veya çıkarmak kolaylaşır.

Bu desen, özellikle isteklerin farklı seviyelerde ele alınması gerektiği durumlarda (örneğin onay süreçleri, teknik destek seviyeleri, loglama veya hata yönetimi gibi) kullanılır. Zincirdeki nesneler esnek şekilde yönetilebilir ve sistemin bakımı kolaylaşır.

Avantajları:

  • Nesneler arasında gevşek bağlılık sağlar.
  • Zincire yeni işlemci eklemek veya çıkarmak kolaydır.
  • İstemci, isteği kimin işlediğini bilmek zorunda değildir.

Dezavantajları:

  • Zincirde bazı istekler işlenmeden sona erebilir.
  • Uzun zincirlerde performans kaybı yaşanabilir.

 

Chain of Responsibility Örnek Senaryo

Bir şehirdeki acil durum çağrı merkezi düşünelim.
Vatandaşlar acil bir durumla karşılaştıklarında çağrı merkezini arar ve yardım isterler.
Çağrı merkezindeki ilk operatör, basit ve hemen çözülebilecek durumları (örneğin bilgi verme veya yönlendirme) doğrudan cevaplar.
Daha teknik veya ciddi bir vaka ise bir üst seviye operatöre aktarılır (örneğin sağlık ekibine yönlendirme).
Eğer konu hâlâ çözülemiyorsa, olay bir üst operatöre (örneğin polis veya itfaiye) iletilir.
Bu şekilde, her acil durum talebi en uygun ve yetkili kişiye zincir halinde iletilir ve çözülür.

Chain of Responsibility C# Kod Örneği

using System;

// Zincir elemanı soyut sınıfı
public abstract class EmergencyHandler
{
    protected EmergencyHandler _nextHandler;

    // Zincire bir sonraki elemanı ekle
    public void SetNextHandler(EmergencyHandler nextHandler)
    {
        _nextHandler = nextHandler;
    }

    // Acil durum talebini işleme yöntemi
    public abstract void HandleRequest(string citizenName, string emergencyType);
}

// Operatör: Basit acil durumları çözer
public class Operator : EmergencyHandler
{
    public override void HandleRequest(string citizenName, string emergencyType)
    {
        if (emergencyType == "bilgi" || emergencyType == "yönlendirme")
        {
            Console.WriteLine($"{citizenName} için '{emergencyType}' talebi OPERATÖR tarafından karşılandı.");
        }
        else if (_nextHandler != null)
        {
            Console.WriteLine($"Operatör '{emergencyType}' talebini SAĞLIK EKİBİNE iletiyor.");
            _nextHandler.HandleRequest(citizenName, emergencyType);
        }
        else
        {
            Console.WriteLine($"'{emergencyType}' talebi için yetkili bulunamadı!");
        }
    }
}

// Sağlık ekibi: Tıbbi acil durumları çözer
public class MedicalTeam : EmergencyHandler
{
    public override void HandleRequest(string citizenName, string emergencyType)
    {
        if (emergencyType == "sağlık" || emergencyType == "yaralanma")
        {
            Console.WriteLine($"{citizenName} için '{emergencyType}' talebi SAĞLIK EKİBİ tarafından karşılandı.");
        }
        else if (_nextHandler != null)
        {
            Console.WriteLine($"Sağlık ekibi '{emergencyType}' talebini POLİSE iletiyor.");
            _nextHandler.HandleRequest(citizenName, emergencyType);
        }
        else
        {
            Console.WriteLine($"'{emergencyType}' talebi için yetkili bulunamadı!");
        }
    }
}

// Polis: Güvenlik veya suç durumlarını çözer
public class Police : EmergencyHandler
{
    public override void HandleRequest(string citizenName, string emergencyType)
    {
        if (emergencyType == "güvenlik" || emergencyType == "suç")
        {
            Console.WriteLine($"{citizenName} için '{emergencyType}' talebi POLİS tarafından karşılandı.");
        }
        else if (_nextHandler != null)
        {
            Console.WriteLine($"Polis '{emergencyType}' talebini İTFAİYEYE iletiyor.");
            _nextHandler.HandleRequest(citizenName, emergencyType);
        }
        else
        {
            Console.WriteLine($"'{emergencyType}' talebi için yetkili bulunamadı!");
        }
    }
}

// İtfaiye: Yangın ve kurtarma acil durumlarını çözer
public class FireDepartment : EmergencyHandler
{
    public override void HandleRequest(string citizenName, string emergencyType)
    {
        if (emergencyType == "yangın" || emergencyType == "kurtarma")
        {
            Console.WriteLine($"{citizenName} için '{emergencyType}' talebi İTFAİYE tarafından karşılandı.");
        }
        else
        {
            Console.WriteLine($"'{emergencyType}' talebi İTFAİYE tarafından işlenemedi.");
        }
    }
}

// Kullanım örneği
class Program
{
    static void Main(string[] args)
    {
        // Zincir oluşturuluyor
        EmergencyHandler operatorHandler = new Operator();
        EmergencyHandler medicalHandler = new MedicalTeam();
        EmergencyHandler policeHandler = new Police();
        EmergencyHandler fireHandler = new FireDepartment();

        operatorHandler.SetNextHandler(medicalHandler);
        medicalHandler.SetNextHandler(policeHandler);
        policeHandler.SetNextHandler(fireHandler);

        // Farklı acil durumlar zincir üzerinden işleniyor
        operatorHandler.HandleRequest("Ayşe", "bilgi");          // Operatör çözer
        Console.WriteLine();
        operatorHandler.HandleRequest("Mehmet", "yaralanma");    // Sağlık ekibi çözer
        Console.WriteLine();
        operatorHandler.HandleRequest("Selin", "suç");           // Polis çözer
        Console.WriteLine();
        operatorHandler.HandleRequest("Can", "yangın");          // İtfaiye çözer
        Console.WriteLine();
        operatorHandler.HandleRequest("Burak", "uzaylı saldırısı"); // Hiçbiri çözemez
    }
}

Command Yazılım Tasarım Deseni

Command (Komut) yazılım tasarım deseni, bir işlemi (komutu) nesne olarak kapsülleyerek, o işlemin ne zaman ve nasıl çalıştırılacağını, geri alınacağını veya sırasının değiştirilebileceğini kolayca yönetmeyi sağlar. Bu desen sayesinde, isteği gönderen ile işlemi gerçekleştiren nesne birbirinden ayrılır; böylece işlemler kuyruklanabilir, loglanabilir veya geri alınabilir (undo-redo).

Command deseni genellikle menü sistemlerinde, işlem geçmişi (undo/redo), makro kaydetme, görev sıralama gibi uygulamalarda tercih edilir. Her komut, kendi başına çalıştırılabilir ve yönetilebilir bir nesne olarak tanımlanır.

Avantajları:

  • İstek gönderen ve işlemi gerçekleştiren nesnenin ayrışmasını sağlar.
  • Komutlar kuyruklanabilir, kaydedilebilir, geri alınabilir.
  • Yeni komut türleri kolayca eklenebilir.
  • Karmaşık işlem zincirleri ve makro yapıları için uygundur.

Dezavantajları:

  • Ekstra nesne ve sınıf üretimi gerektirir.
  • Basit işlemler için fazla karmaşıklık oluşturabilir.

Command Örnek Senaryo

Bir akıllı ev sistemi düşünelim.
Kullanıcı, uzaktan kumanda ile evdeki farklı cihazlara (lamba, klima, perde) çeşitli komutlar gönderebiliyor.
Her cihaz farklı komutları uyguluyor (aç, kapat, ayarla).
Kumanda, her komutu bir nesne olarak saklıyor; böylece işlemler geri alınabiliyor (örneğin son açılan lamba tekrar kapatılabiliyor), komutlar sıralanabiliyor veya toplu olarak uygulanabiliyor.
Bu sistem, ev otomasyonunda esnek ve yönetilebilir bir komut altyapısı sunuyor.

Command C# Kod Örneği

using System;
using System.Collections.Generic;

// Komut arayüzü
public interface ICommand
{
    void Execute();    // Komutu çalıştır
    void Undo();       // Komutu geri al
}

// Alıcı (Receiver): Lamba
public class Light
{
    public void TurnOn()
    {
        Console.WriteLine("Lamba açıldı.");
    }
    public void TurnOff()
    {
        Console.WriteLine("Lamba kapatıldı.");
    }
}

// Komut: Lambayı aç
public class LightOnCommand : ICommand
{
    private Light _light;

    public LightOnCommand(Light light)
    {
        _light = light;
    }

    public void Execute()
    {
        _light.TurnOn();
    }

    public void Undo()
    {
        _light.TurnOff();
    }
}

// Komut: Lambayı kapat
public class LightOffCommand : ICommand
{
    private Light _light;

    public LightOffCommand(Light light)
    {
        _light = light;
    }

    public void Execute()
    {
        _light.TurnOff();
    }

    public void Undo()
    {
        _light.TurnOn();
    }
}

// Alıcı (Receiver): Klima
public class AirConditioner
{
    public void Start()
    {
        Console.WriteLine("Klima çalıştırıldı.");
    }
    public void Stop()
    {
        Console.WriteLine("Klima kapatıldı.");
    }
}

// Komut: Klimayı aç
public class AirConditionerOnCommand : ICommand
{
    private AirConditioner _ac;

    public AirConditionerOnCommand(AirConditioner ac)
    {
        _ac = ac;
    }

    public void Execute()
    {
        _ac.Start();
    }

    public void Undo()
    {
        _ac.Stop();
    }
}

// Komut: Klimayı kapat
public class AirConditionerOffCommand : ICommand
{
    private AirConditioner _ac;

    public AirConditionerOffCommand(AirConditioner ac)
    {
        _ac = ac;
    }

    public void Execute()
    {
        _ac.Stop();
    }

    public void Undo()
    {
        _ac.Start();
    }
}

// Kumanda (Invoker)
public class RemoteControl
{
    private Stack<ICommand> _commandHistory = new Stack<ICommand>();

    // Komutu uygula ve geçmişe ekle
    public void PressButton(ICommand command)
    {
        command.Execute();
        _commandHistory.Push(command);
    }

    // Son komutu geri al
    public void PressUndo()
    {
        if (_commandHistory.Count > 0)
        {
            ICommand lastCommand = _commandHistory.Pop();
            lastCommand.Undo();
        }
        else
        {
            Console.WriteLine("Geri alınacak komut yok.");
        }
    }
}

// Kullanım örneği
class Program
{
    static void Main(string[] args)
    {
        Light livingRoomLight = new Light();
        AirConditioner ac = new AirConditioner();

        ICommand lightOn = new LightOnCommand(livingRoomLight);
        ICommand lightOff = new LightOffCommand(livingRoomLight);
        ICommand acOn = new AirConditionerOnCommand(ac);
        ICommand acOff = new AirConditionerOffCommand(ac);

        RemoteControl remote = new RemoteControl();

        // Kullanıcı işlemleri
        remote.PressButton(lightOn);   // Lamba açıldı.
        remote.PressButton(acOn);      // Klima çalıştırıldı.
        remote.PressButton(lightOff);  // Lamba kapatıldı.

        Console.WriteLine("\nSon işlemleri geri alıyoruz:");

        remote.PressUndo(); // Lambayı tekrar açar
        remote.PressUndo(); // Klimayı kapatır
        remote.PressUndo(); // Lambayı kapatır
        remote.PressUndo(); // Geri alınacak komut yok.
    }
}

Interpreter Yazılım Tasarım Deseni

Interpreter (Yorumlayıcı) yazılım tasarım deseni, bir dilin kurallarını ve ifadelerini nesnelere dönüştürerek bu ifadelerin çözümlenmesini ve çalıştırılmasını sağlar. Özellikle kendi komut diliyle çalışan uygulamalarda, arama filtrelerinde, matematiksel ifadelerde veya küçük bir dil motoru oluşturmak istediğinizde kullanılır.
Her bir ifade veya kural bir nesne olarak temsil edilir ve yorumlanır. Bu yaklaşım, dille ilgili yeni kurallar veya ifadeler eklemeyi kolaylaştırır ve kodun esnekliğini artırır.

Avantajları:

  • Yeni ifade türleri kolayca eklenebilir.
  • Dilin kuralları doğrudan sınıflar ile modellenebilir.
  • Karmaşık ifadeler nesneler üzerinden esnek biçimde çözümlenebilir.

Dezavantajları:

  • Büyük ve karmaşık dillerde performans kaybı yaşanabilir.
  • Kod karmaşıklığı artabilir.

Interpreter Örnek Senaryo

Bir kütüphanede akıllı bir kitap arama paneli olduğunu düşünelim.
Okuyucular, “yazar:Orhan Pamuk ve yıl>2000” gibi ifadelerle istedikleri kitapları sorgulayabiliyor.
Her sorgu, bir “yorumlayıcı” tarafından çözümleniyor ve aranan kriterlere uyan kitaplar ekranda listeleniyor.
Sistem; yazar, yıl, tür gibi filtreleri ve “ve”, “veya”, “değil” gibi mantıksal operatörleri destekliyor.
Bu sayede kullanıcılar, doğal bir dilde yazılmış mantıksal arama ifadeleriyle kolayca kitap bulabiliyor.

Interpreter C# Kod Örneği

using System;
using System.Collections.Generic;
using System.Linq;

// Kitap modeli
public class Book
{
    public string Author { get; set; }
    public int Year { get; set; }
    public string Genre { get; set; }
    public string Title { get; set; }
}

// Soyut ifade (Abstract Expression)
public interface IBookExpression
{
    bool Interpret(Book book);
}

// Yazar filtresi (Terminal Expression)
public class AuthorExpression : IBookExpression
{
    private string _author;
    public AuthorExpression(string author) { _author = author.ToLower(); }
    public bool Interpret(Book book) { return book.Author.ToLower().Contains(_author); }
}

// Yıl filtresi (Terminal Expression)
public class YearGreaterExpression : IBookExpression
{
    private int _year;
    public YearGreaterExpression(int year) { _year = year; }
    public bool Interpret(Book book) { return book.Year > _year; }
}

// Tür filtresi (Terminal Expression)
public class GenreExpression : IBookExpression
{
    private string _genre;
    public GenreExpression(string genre) { _genre = genre.ToLower(); }
    public bool Interpret(Book book) { return book.Genre.ToLower().Contains(_genre); }
}

// Mantıksal VE (Non-terminal Expression)
public class AndExpression : IBookExpression
{
    private IBookExpression _left, _right;
    public AndExpression(IBookExpression left, IBookExpression right)
    {
        _left = left; _right = right;
    }
    public bool Interpret(Book book) { return _left.Interpret(book) && _right.Interpret(book); }
}

// Mantıksal VEYA (Non-terminal Expression)
public class OrExpression : IBookExpression
{
    private IBookExpression _left, _right;
    public OrExpression(IBookExpression left, IBookExpression right)
    {
        _left = left; _right = right;
    }
    public bool Interpret(Book book) { return _left.Interpret(book) || _right.Interpret(book); }
}

// Mantıksal DEĞİL (Non-terminal Expression)
public class NotExpression : IBookExpression
{
    private IBookExpression _expr;
    public NotExpression(IBookExpression expr) { _expr = expr; }
    public bool Interpret(Book book) { return !_expr.Interpret(book); }
}

// Basit bir parser: "yazar:Orhan Pamuk ve yıl>2000" gibi ifadeleri çözer
public class LibrarySearchParser
{
    public static IBookExpression Parse(string query)
    {
        // Sadece basit örnekler için: yazar:X ve yıl>Y, tür:X veya yazar:Y gibi
        query = query.ToLower();
        if (query.Contains(" ve "))
        {
            var parts = query.Split(new[] { " ve " }, StringSplitOptions.RemoveEmptyEntries);
            return new AndExpression(Parse(parts[0]), Parse(parts[1]));
        }
        if (query.Contains(" veya "))
        {
            var parts = query.Split(new[] { " veya " }, StringSplitOptions.RemoveEmptyEntries);
            return new OrExpression(Parse(parts[0]), Parse(parts[1]));
        }
        if (query.Contains(" değil"))
        {
            var part = query.Replace(" değil", "").Trim();
            return new NotExpression(Parse(part));
        }
        if (query.StartsWith("yazar:"))
        {
            return new AuthorExpression(query.Replace("yazar:", "").Trim());
        }
        if (query.StartsWith("tür:"))
        {
            return new GenreExpression(query.Replace("tür:", "").Trim());
        }
        if (query.StartsWith("yıl>"))
        {
            int year = int.Parse(query.Replace("yıl>", "").Trim());
            return new YearGreaterExpression(year);
        }
        throw new Exception("Desteklenmeyen ifade!");
    }
}

// Kullanım örneği
class Program
{
    static void Main(string[] args)
    {
        // Kütüphanedeki örnek kitaplar
        var books = new List<Book>
        {
            new Book{ Author="Orhan Pamuk", Year=2002, Genre="Roman", Title="Kar" },
            new Book{ Author="Sabahattin Ali", Year=1943, Genre="Roman", Title="Kürk Mantolu Madonna" },
            new Book{ Author="Ahmet Ümit", Year=2010, Genre="Polisiye", Title="Beyoğlu'nun En Güzel Abisi" },
            new Book{ Author="Orhan Pamuk", Year=1998, Genre="Roman", Title="Benim Adım Kırmızı" }
        };

        // Sorgular
        var queries = new List<string>
        {
            "yazar:Orhan Pamuk ve yıl>2000",
            "tür:roman veya yazar:ahmet ümit",
            "yazar:Sabahattin Ali değil"
        };

        foreach (var query in queries)
        {
            Console.WriteLine($"\nArama: {query}");
            var expr = LibrarySearchParser.Parse(query);
            var result = books.Where(b => expr.Interpret(b)).ToList();
            if (result.Count == 0)
                Console.WriteLine("Sonuç bulunamadı.");
            else
                foreach (var book in result)
                    Console.WriteLine($"{book.Title} - {book.Author} ({book.Year}) [{book.Genre}]");
        }
    }
}

Copilot said: ### Iterator Yazılım Tasarım Deseni Iterator (Gezgin/İteratör) yazılım

Iterator Yazılım Tasarım Deseni

Iterator (Gezgin/İteratör) yazılım tasarım deseni, bir koleksiyonun elemanları üzerinde, koleksiyonun iç yapısını bilmeden sıralı ve güvenli şekilde dolaşmayı (iterate etmeyi) sağlar. Bu desen sayesinde, bir nesne dizisi üzerinde gezinti işlemleri merkezi bir noktada toplanır ve koleksiyonun farklı türleri için ortak bir gezinti arayüzü sunulur.
Koleksiyonun elemanlarına erişim, ekleme veya silme işlemleri dışında, sadece gezinmek için kullanılır. Iterator deseni, özellikle karmaşık veri yapılarında veri bütünlüğünü ve erişim kolaylığını artırır.

Avantajları:

  • Koleksiyonun iç yapısından bağımsız dolaşım sağlar.
  • Farklı koleksiyon tiplerine aynı gezinti arayüzü sunar.
  • Tek yönlü veya çift yönlü gezinti kolayca eklenebilir.
  • Aynı koleksiyon üzerinde birden fazla iterator ile farklı gezintiler yapılabilir.

Dezavantajları:

  • Ekstra sınıf ve nesne üretimine neden olabilir.
  • Basit koleksiyonlar için gereksiz karmaşıklık oluşturabilir.

Iterator Örnek Senaryo

Bir sanat galerisinde ziyaretçiler, galerideki sanat eserlerini gezmek için dijital bir rehber kullanıyor.
Her eser bir koleksiyonun parçası ve ziyaretçi, rehberde "sonraki eser", "önceki eser" butonları ile koleksiyondaki sanat eserleri arasında sırayla ilerleyebiliyor.
Ziyaretçiler, galerinin yapısını veya eserlerin nasıl saklandığını bilmeden, rehber sayesinde tüm eserleri rahatça gezebiliyor.
Bu sistem, Iterator desenini kullanarak sanat eserleri koleksiyonunda kolay ve kontrollü bir gezinme sunuyor.

Iterator C# Kod Örneği

using System;
using System.Collections.Generic;

// Sanat eseri nesnesi
public class ArtWork
{
    public string Title { get; set; }
    public string Artist { get; set; }

    public ArtWork(string title, string artist)
    {
        Title = title;
        Artist = artist;
    }
}

// Koleksiyon arayüzü
public interface IArtCollection
{
    IArtIterator CreateIterator();
}

// Iterator arayüzü
public interface IArtIterator
{
    bool HasNext();              // Sonraki eleman var mı?
    ArtWork Next();              // Sonraki elemanı getir
    bool HasPrevious();          // Önceki eleman var mı?
    ArtWork Previous();          // Önceki elemanı getir
}

// Galeri koleksiyonu
public class ArtGallery : IArtCollection
{
    private List<ArtWork> _artWorks = new List<ArtWork>();

    public void AddArtWork(ArtWork artWork)
    {
        _artWorks.Add(artWork);
    }

    public IArtIterator CreateIterator()
    {
        return new ArtGalleryIterator(_artWorks);
    }
}

// Galeri iteratoru
public class ArtGalleryIterator : IArtIterator
{
    private List<ArtWork> _artWorks;
    private int _position = -1; // Başlangıçta koleksiyonun öncesinde

    public ArtGalleryIterator(List<ArtWork> artWorks)
    {
        _artWorks = artWorks;
    }

    public bool HasNext()
    {
        return _position < _artWorks.Count - 1;
    }

    public ArtWork Next()
    {
        if (HasNext())
        {
            _position++;
            return _artWorks[_position];
        }
        return null;
    }

    public bool HasPrevious()
    {
        return _position > 0;
    }

    public ArtWork Previous()
    {
        if (HasPrevious())
        {
            _position--;
            return _artWorks[_position];
        }
        return null;
    }
}

// Kullanım örneği
class Program
{
    static void Main(string[] args)
    {
        // Sanat galerisi oluşturuluyor
        ArtGallery gallery = new ArtGallery();
        gallery.AddArtWork(new ArtWork("Yıldızlı Gece", "Vincent van Gogh"));
        gallery.AddArtWork(new ArtWork("Düşünen Adam", "Auguste Rodin"));
        gallery.AddArtWork(new ArtWork("İnci Küpeli Kız", "Johannes Vermeer"));

        IArtIterator iterator = gallery.CreateIterator();

        Console.WriteLine("Sanat Galerisi Gezintisi (İleri):");
        while (iterator.HasNext())
        {
            ArtWork art = iterator.Next();
            Console.WriteLine($"{art.Title} - {art.Artist}");
        }

        Console.WriteLine("\nSanat Galerisi Gezintisi (Geri):");
        while (iterator.HasPrevious())
        {
            ArtWork art = iterator.Previous();
            Console.WriteLine($"{art.Title} - {art.Artist}");
        }
    }
}

Mediator Yazılım Tasarım Deseni

Mediator (Arabulucu) yazılım tasarım deseni, bir sistemdeki farklı nesneler arasındaki iletişimi merkezi bir arabulucu üzerinden yöneterek kodun karmaşıklığını azaltır. Nesneler birbirleriyle doğrudan iletişim kurmak yerine, tüm mesajlarını ve işlemlerini arabulucuya iletir. Arabulucu, bu mesajları uygun nesnelere yönlendirir. Böylece nesneler arasındaki bağımlılık azalır, sistemin bakımı ve genişletilmesi kolaylaşır.
Mediator deseni; özellikle kullanıcı arayüzlerinde, çoklu bileşenin koordineli çalışması gerektiği durumlarda veya karmaşık iş süreçlerinde tercih edilir.

Avantajları:

  • Nesneler arasındaki bağımlılığı azaltır.
  • İletişim merkezi bir noktadan yönetildiği için sistemin bakımı kolaylaşır.
  • Karmaşık etkileşimler sadeleşir ve kod okunabilirliği artar.

Dezavantajları:

  • Arabulucu nesnesi çok fazla sorumluluk üstlenirse karmaşıklaşabilir.
  • Küçük sistemlerde gereksiz bir soyutlama katmanı olabilir.

Mediator Örnek Senaryo

Bir kahve dükkanında müşteri sipariş verdiğinde, kasiyer siparişi alır ve arabulucuya bildirir. Arabulucu baristaya haber verir, barista kahveyi hazırlar ve arabulucu tekrar kasiyere ödeme alınması gerektiğini iletir.
Her bileşen sadece arabulucu ile iletişim kurar, birbirinin işleyişini bilmez.

Mediator C# Kod Örneği

using System;

// Arabulucu arayüzü
public interface ICafeMediator
{
    void Notify(object sender, string @event);
}

// Concrete Mediator: Kahve dükkanı arabulucu
public class CafeMediator : ICafeMediator
{
    private Barista _barista;
    private Cashier _cashier;

    public CafeMediator(Barista barista, Cashier cashier)
    {
        _barista = barista;
        _barista.SetMediator(this);
        _cashier = cashier;
        _cashier.SetMediator(this);
    }

    // Olaylara göre ilgili işlemi başlatır
    public void Notify(object sender, string @event)
    {
        if (@event == "OrderReceived")
        {
            Console.WriteLine("Mediator: Sipariş alındı, baristaya bildiriliyor.");
            _barista.PrepareCoffee();
        }
        if (@event == "CoffeeReady")
        {
            Console.WriteLine("Mediator: Kahve hazır, kasiyere bildiriliyor.");
            _cashier.TakePayment();
        }
    }
}

// Temel bileşen: Arabulucu referansını tutar
public class CafeComponent
{
    protected ICafeMediator _mediator;

    public CafeComponent(ICafeMediator mediator = null)
    {
        _mediator = mediator;
    }

    public void SetMediator(ICafeMediator mediator)
    {
        _mediator = mediator;
    }
}

// Barista sınıfı
public class Barista : CafeComponent
{
    public void PrepareCoffee()
    {
        Console.WriteLine("Barista: Kahveyi hazırlıyor.");
        _mediator.Notify(this, "CoffeeReady");
    }
}

// Kasiyer sınıfı
public class Cashier : CafeComponent
{
    public void ReceiveOrder()
    {
        Console.WriteLine("Kasiyer: Siparişi alıyor.");
        _mediator.Notify(this, "OrderReceived");
    }

    public void TakePayment()
    {
        Console.WriteLine("Kasiyer: Ödeme alınıyor.");
    }
}

// Kullanım örneği
class Program
{
    static void Main(string[] args)
    {
        Barista barista = new Barista();
        Cashier cashier = new Cashier();
        CafeMediator mediator = new CafeMediator(barista, cashier);

        Console.WriteLine("Müşteri kahve siparişi veriyor.");
        cashier.ReceiveOrder();
    }
}

Memento Yazılım Tasarım Deseni

Memento (Hatıra/Kayıt) yazılım tasarım deseni, bir nesnenin belirli bir andaki durumunu dış dünyaya açmadan kaydeder ve gerektiğinde bu duruma geri dönmesini sağlar. Özellikle “geri al” (undo), “geri yükle” veya “önceki duruma dön” gibi işlemlerin gerektiği uygulamalarda kullanılır.
Memento deseninde üç ana rol bulunur:

  • Originator (Kaynak): Durumu oluşturan ve değiştiren nesne.
  • Memento (Kayıt): Saklanan durumun temsilcisi olan nesne.
  • Caretaker (Bakıcı): Mementoları tutan ve geri alma/yükleme işlemlerini yöneten nesne.

Avantajları:

  • Nesnenin iç durumu dışarıya açılmadan saklanır ve geri yüklenir.
  • Geri alma/yeniden yükleme gibi işlemleri kolaylaştırır.
  • Kodun esnekliğini ve bakımı kolaylaştırır.

Dezavantajları:

  • Çok fazla memento nesnesi oluşturulursa hafıza yükü artabilir.
  • Karmaşık nesne durumlarında yönetim zorlaşabilir.

Memento Örnek Senaryo

Bir notepad (not defteri) uygulamasında kullanıcı metin yazıyor, siliyor veya değişiklikler yapıyor.
Her değişiklikten sonra uygulama, metnin bir “memento”sunu saklıyor.
Kullanıcı yanlışlıkla bir satırı sildiğinde veya hatalı bir değişiklik yaptığında “geri al” butonuna basarak önceki metne kolayca dönebiliyor.
Her “geri al” işlemiyle not defteri, bir önceki kaydedilen duruma geri dönüyor.
Bu sayede kullanıcı, adım adım geçmişe dönebilir ve yaptığı hatalardan kolayca kurtulabilir.

Memento C# Kod Örneği

using System;
using System.Collections.Generic;

// Notepad uygulamasının ana metin tutucu nesnesi (Originator)
public class Notepad
{
    public string Text { get; set; } // Not defterinin içeriği

    public Notepad(string initialText)
    {
        Text = initialText;
    }

    // Anlık durumun mementosunu oluşturur
    public NotepadMemento Save()
    {
        return new NotepadMemento(Text);
    }

    // Memento ile durumu geri yükler
    public void Restore(NotepadMemento memento)
    {
        Text = memento.GetState();
    }
}

// Memento nesnesi: Not defterinin durumunu saklar
public class NotepadMemento
{
    private readonly string _state;
    public NotepadMemento(string state)
    {
        _state = state;
    }
    public string GetState()
    {
        return _state;
    }
}

// Geri al/yönetim nesnesi (Caretaker): Mementoları tutar
public class UndoHistory
{
    private Stack<NotepadMemento> _history = new Stack<NotepadMemento>();

    public void Save(NotepadMemento memento)
    {
        _history.Push(memento);
    }

    public NotepadMemento Undo()
    {
        if (_history.Count > 0)
            return _history.Pop();
        return null;
    }
}

// Kullanım örneği
class Program
{
    static void Main(string[] args)
    {
        UndoHistory history = new UndoHistory();
        Notepad notepad = new Notepad("Merhaba, ilk notunuz!");

        // İlk metin kaydediliyor
        history.Save(notepad.Save());

        notepad.Text += "\nBugün yapılacaklar: Kod yaz.";
        history.Save(notepad.Save());

        notepad.Text += "\nUnutma: Blog yazısını tamamla.";
        history.Save(notepad.Save());

        Console.WriteLine("Not Defteri Şu Anki Durum:");
        Console.WriteLine(notepad.Text);

        // Kullanıcı yanlışlıkla son satırı siliyor ve geri almak istiyor
        Console.WriteLine("\nGeri Al butonuna basıldı...");
        NotepadMemento previous = history.Undo();
        if (previous != null)
            notepad.Restore(previous);
        Console.WriteLine(notepad.Text);

        // Bir kez daha geri al
        Console.WriteLine("\nTekrar Geri Al butonuna basıldı...");
        previous = history.Undo();
        if (previous != null)
            notepad.Restore(previous);
        Console.WriteLine(notepad.Text);
    }
}

Observer Yazılım Tasarım Deseni

Observer (Gözlemci) yazılım tasarım deseni, bir nesnede (subject) meydana gelen değişikliklerin, bu nesneye bağlı (abone olan) diğer nesnelere (observers/gözlemciler) otomatik olarak bildirilmesini sağlar.
Bu desen sayesinde, bir olay veya durum değişikliği gerçekleştiğinde, ona abone olan tüm gözlemciler otomatik olarak haberdar edilir ve kendi işlemlerini gerçekleştirebilirler.
Özellikle GUI (grafik arayüz), bildirim sistemleri, veri akışları, haberleşme altyapıları, oyunlar ve zamanlayıcılar gibi dinamik yapıların yönetiminde kullanılır.

Avantajları:

  • Nesneler arasında gevşek bağlılık sağlar.
  • Dinamik olarak gözlemci ekleme/çıkarma yapılabilir.
  • Bir olay gerçekleştiğinde birden fazla nesneye otomatik bildirim yapılabilir.

Dezavantajları:

  • Çok fazla gözlemci olduğunda performans ve karmaşıklık artabilir.
  • Bildirim sırası garanti edilmez.

Observer Örnek Senaryo

Bir web uygulamasında kullanıcı sisteme kayıt olduğunda, ona otomatik olarak hoş geldin e-postası gitmesini istiyoruz.
Kullanıcı kayıt işlemi (subject) tamamlandığında, e-posta servisi (observer) bu olaydan haberdar olur ve ilgili kullanıcıya e-posta gönderir.
İleride başka servisler (örneğin; SMS veya bildirim servisleri) de sisteme eklenerek, kullanıcı kayıt olduğunda otomatik tetiklenebilir.
Bu sayede sistem genişletilebilir ve yeni gözlemciler kolayca eklenebilir.

Observer C# Kod Örneği

using System;
using System.Collections.Generic;

// Subject: Kullanıcı kayıt sistemi
public class UserRegistration
{
    private List<IUserObserver> _observers = new List<IUserObserver>();

    // Gözlemci ekleme
    public void Attach(IUserObserver observer)
    {
        _observers.Add(observer);
    }

    // Gözlemci çıkarma
    public void Detach(IUserObserver observer)
    {
        _observers.Remove(observer);
    }

    // Kullanıcı kaydı ve gözlemcilere bildirim
    public void RegisterUser(string username, string email)
    {
        Console.WriteLine($"Kullanıcı kaydı tamamlandı: {username} ({email})");
        Notify(username, email);
    }

    // Tüm gözlemcilere haber ver
    private void Notify(string username, string email)
    {
        foreach (var observer in _observers)
        {
            observer.Update(username, email);
        }
    }
}

// Observer arayüzü
public interface IUserObserver
{
    void Update(string username, string email);
}

// Concrete Observer: Email servis
public class EmailService : IUserObserver
{
    public void Update(string username, string email)
    {
        Console.WriteLine($"EmailService: {username} kullanıcısına hoş geldin e-postası gönderildi ({email}).");
    }
}

// Concrete Observer: SMS servis (Genişletilebilirlik için örnek)
public class SmsService : IUserObserver
{
    public void Update(string username, string email)
    {
        Console.WriteLine($"SmsService: {username} kullanıcısına hoş geldin SMS'i gönderildi.");
    }
}

// Kullanım örneği
class Program
{
    static void Main(string[] args)
    {
        UserRegistration registration = new UserRegistration();

        // Email ve SMS servisleri sisteme abone oluyor
        registration.Attach(new EmailService());
        registration.Attach(new SmsService());

        // Kullanıcı kaydı gerçekleşiyor, servisler otomatik tetikleniyor
        registration.RegisterUser("ayzdru", "ayzdru@example.com");

        // Sadece email servisi ile devam etmek istersek
        registration.Detach(new SmsService());
        registration.RegisterUser("janedoe", "janedoe@example.com");
    }
}

State Yazılım Tasarım Deseni

State (Durum) yazılım tasarım deseni, bir nesnenin davranışlarını kendi iç durumuna göre değiştirmesini sağlar. Yani nesne, farklı durumlarda farklı şekilde davranır ve bu davranışlar kodda açıkça ayrıştırılmış olur.
State deseni, karmaşık “if-else” veya “switch-case” bloklarını ortadan kaldırarak, her durumu ayrı bir sınıfa taşır ve nesne çalışma zamanında durumunu değiştirerek davranışlarını otomatik olarak günceller.
Bu desen; iş akışları, süreç yönetimi, oyunlar, otomasyon, kullanıcı etkileşimleri veya bir nesnenin birden fazla aşamadan geçtiği sistemlerde sıkça kullanılır.

Avantajları:

  • Karmaşık durum yönetimini sadeleştirir.
  • Kodun okunabilirliğini ve bakımını kolaylaştırır.
  • Durum değişikliklerinde kodun esnekliğini artırır.

Dezavantajları:

  • Her durum için ayrı sınıflar gerektiğinden dosya sayısı artabilir.
  • Küçük projelerde gereksiz soyutlama oluşturabilir.

State Örnek Senaryo

Bir trafik lambası düşünelim.
Trafik lambasının üç farklı durumu vardır: Kırmızı (Red), Sarı (Yellow), Yeşil (Green).
Her durumda lambanın davranışı değişir: Kırmızıda araçlar durur, yeşilde geçer, sarıda hazırlanır.
Trafik lambası kendi durumunu değiştirerek, her aşamada farklı bir davranış sergiler.
Bu akıştaki tüm geçişler ve davranışlar State desenine uygun olarak kodlanır, böylece sistem hem genişletilebilir hem de bakımı kolay olur. 

State C# Kod Örneği

using System;

// State arayüzü: Trafik lambasının farklı durumlarının davranışlarını tanımlar
public interface ITrafficLightState
{
    void Handle(TrafficLight context);
}

// Kırmızı ışık durumu
public class RedState : ITrafficLightState
{
    public void Handle(TrafficLight context)
    {
        Console.WriteLine("Trafik Lambası: Kırmızı - Araçlar duruyor.");
        context.SetState(new GreenState()); // Sonraki durum yeşil
    }
}

// Yeşil ışık durumu
public class GreenState : ITrafficLightState
{
    public void Handle(TrafficLight context)
    {
        Console.WriteLine("Trafik Lambası: Yeşil - Araçlar geçiyor.");
        context.SetState(new YellowState()); // Sonraki durum sarı
    }
}

// Sarı ışık durumu
public class YellowState : ITrafficLightState
{
    public void Handle(TrafficLight context)
    {
        Console.WriteLine("Trafik Lambası: Sarı - Araçlar hazırlanıyor.");
        context.SetState(new RedState()); // Sonraki durum kırmızı
    }
}

// Context: Trafik lambası nesnesi
public class TrafficLight
{
    private ITrafficLightState _state;

    public TrafficLight()
    {
        // Başlangıç durumu kırmızı
        _state = new RedState();
    }

    // Durumu güncelle
    public void SetState(ITrafficLightState state)
    {
        _state = state;
    }

    // Şu anki durumu işle
    public void Change()
    {
        _state.Handle(this);
    }
}

// Kullanım örneği
class Program
{
    static void Main(string[] args)
    {
        TrafficLight light = new TrafficLight();

        // Trafik lambası 6 kez değişiyor (Kırmızı → Yeşil → Sarı döngüsü)
        for (int i = 0; i < 6; i++)
        {
            light.Change();
        }
    }
}

Strategy Yazılım Tasarım Deseni

Strategy (Strateji) yazılım tasarım deseni, bir nesnenin davranışını çalışma zamanında dinamik olarak değiştirmek için kullanılır. Davranışları, birbirinin yerine geçebilen ve aynı arayüzü (interface) paylaşan ayrı strateji sınıflarına taşır.
Bu desen sayesinde farklı algoritmalar veya işlemler, ana nesneye (context) bağlı olmadan kolayca değiştirilebilir, genişletilebilir veya seçilebilir.
Özellikle farklı iş kuralları, ödeme yöntemleri, sıralama algoritmaları veya veri işleme stratejilerinin gerektiği sistemlerde tercih edilir.

Avantajları:

  • Farklı algoritmalar kolayca eklenebilir ve değiştirilebilir.
  • Kodun bakımı ve genişletilmesi kolaylaşır.
  • Ana nesne ile algoritma arasında gevşek bağlılık sağlanır.

Dezavantajları:

  • Her strateji için ayrı sınıflar gerektiğinden dosya sayısı artabilir.
  • Küçük projelerde gereksiz soyutlama oluşturabilir.

Strategy Örnek Senaryo

Bir kargo şirketi düşünelim.
Müşteri, gönderisini farklı teslimat stratejileriyle gönderebilir:

  • Standart teslimat (en ucuz, en yavaş)
  • Hızlı teslimat (daha pahalı, daha hızlı)
  • Ekspres teslimat (en hızlı, en pahalı)
    Müşteri gönderi oluştururken istediği teslimat stratejisini seçer. Sistem, seçilen stratejiye göre fiyatı hesaplar ve tahmini teslimat süresini belirler.
    Her strateji ayrı bir algoritma ile çalışır ve yeni bir teslimat türü kolayca eklenebilir.

Strategy C# Kod Örneği

using System;

// Strategy arayüzü: Farklı teslimat algoritmalarını tanımlar
public interface IShippingStrategy
{
    void CalculateShipping(string packageInfo);
}

// Standart teslimat stratejisi
public class StandardShipping : IShippingStrategy
{
    public void CalculateShipping(string packageInfo)
    {
        Console.WriteLine($"Standart Teslimat: {packageInfo} için fiyat 50 TL, teslimat süresi 5 gün.");
    }
}

// Hızlı teslimat stratejisi
public class FastShipping : IShippingStrategy
{
    public void CalculateShipping(string packageInfo)
    {
        Console.WriteLine($"Hızlı Teslimat: {packageInfo} için fiyat 100 TL, teslimat süresi 2 gün.");
    }
}

// Ekspres teslimat stratejisi
public class ExpressShipping : IShippingStrategy
{
    public void CalculateShipping(string packageInfo)
    {
        Console.WriteLine($"Ekspres Teslimat: {packageInfo} için fiyat 200 TL, teslimat süresi 1 gün.");
    }
}

// Context: Gönderi nesnesi, stratejiye göre davranır
public class Shipment
{
    private IShippingStrategy _strategy;

    public Shipment(IShippingStrategy strategy)
    {
        _strategy = strategy;
    }

    // Stratejiyi değiştir
    public void SetStrategy(IShippingStrategy strategy)
    {
        _strategy = strategy;
    }

    // Teslimat hesaplamasını stratejiye göre uygula
    public void ApplyShipping(string packageInfo)
    {
        _strategy.CalculateShipping(packageInfo);
    }
}

// Kullanım örneği
class Program
{
    static void Main(string[] args)
    {
        string package = "Laptop Paketi";

        // Standart teslimat ile gönderiliyor
        Shipment shipment = new Shipment(new StandardShipping());
        shipment.ApplyShipping(package);

        // Müşteri hızlı teslimat isterse strateji değişir
        shipment.SetStrategy(new FastShipping());
        shipment.ApplyShipping(package);

        // En hızlı teslimat seçilirse strateji yine değişir
        shipment.SetStrategy(new ExpressShipping());
        shipment.ApplyShipping(package);
    }
}

Template Method Yazılım Tasarım Deseni

Template Method (Şablon Metot) yazılım tasarım deseni, bir algoritmanın iskeletini (adımlarını) bir temel sınıfta tanımlar, bazı adımların detaylarını ise alt sınıflara bırakır.
Yani algoritmanın genel akışı sabit kalırken, bazı adımlar alt sınıflar tarafından özelleştirilebilir.
Bu desen sayesinde, tekrar eden süreçlerde ortak akış korunur ve alt sınıflar sadece değişken adımları tanımlar.
Özellikle benzer işlemlerin farklı detaylarla uygulanması gereken durumlarda, kodun yeniden kullanılabilirliğini ve bakımını kolaylaştırır.

Avantajları:

  • Ortak algoritma adımları merkezi olarak yönetilir.
  • Alt sınıflar sadece değişken adımları yazar, tekrar önlenir.
  • Kodun bakım ve genişletilmesi kolaylaşır.

Dezavantajları:

  • Fazla soyutlama küçük projelerde karmaşıklık yaratabilir.
  • Alt sınıfların algoritma akışını değiştirmesi istenmiyorsa dikkatli olunmalı.

Template Method Örnek Senaryo

Bir spor salonunda, farklı grup egzersiz dersleri düzenleniyor.
Her dersin başlangıç süreci benzer adımlardan oluşuyor:

  1. Katılımcıların kaydı alınır
  2. Ortak ısınma hareketleri yapılır
  3. Derse özel ana egzersiz uygulanır (örneğin; yoga dersi için meditasyon, pilates için core çalışması)
  4. Soğuma hareketleri yapılır
  5. Katılımcılara teşekkür edilir
    Burada; katılımcı kaydı, ısınma ve soğuma hareketleri ile teşekkür adımları tüm derslerde aynıdır.
    Derse özel ana egzersiz ise her derse göre değişiklik gösterir.
    Template Method ile dersin genel akışı ana sınıfta tanımlanır, ana egzersiz adımı ise alt sınıflarda özelleştirilir.

Template Method C# Kod Örneği

using System;

// Template Method: Dersin adımlarının iskeleti
public abstract class GymClass
{
    // Template Method: Dersin genel akışını tanımlar
    public void StartClass()
    {
        RegisterParticipants();
        WarmUp();
        MainExercise();      // Alt sınıflar tarafından özelleştirilecek adım
        CoolDown();
        ThankParticipants();
    }

    // Ortak adımlar - tüm dersler için aynı
    private void RegisterParticipants()
    {
        Console.WriteLine("Katılımcıların kaydı alındı.");
    }

    private void WarmUp()
    {
        Console.WriteLine("Ortak ısınma hareketleri yapıldı.");
    }

    private void CoolDown()
    {
        Console.WriteLine("Soğuma hareketleri yapıldı.");
    }

    private void ThankParticipants()
    {
        Console.WriteLine("Katılımcılara teşekkür edildi.");
    }

    // Soyut adım - derse göre değişir
    protected abstract void MainExercise();
}

// Concrete Class: Yoga dersi
public class YogaClass : GymClass
{
    protected override void MainExercise()
    {
        Console.WriteLine("Yoga için meditasyon ve esneme çalışması yapıldı.");
    }
}

// Concrete Class: Pilates dersi
public class PilatesClass : GymClass
{
    protected override void MainExercise()
    {
        Console.WriteLine("Pilates için core ve denge egzersizleri yapıldı.");
    }
}

// Kullanım örneği
class Program
{
    static void Main(string[] args)
    {
        GymClass yoga = new YogaClass();
        Console.WriteLine("Yoga Dersi Başlıyor:");
        yoga.StartClass();

        Console.WriteLine("\nPilates Dersi Başlıyor:");
        GymClass pilates = new PilatesClass();
        pilates.StartClass();
    }
}

Visitor Yazılım Tasarım Deseni

Visitor (Ziyaretçi) yazılım tasarım deseni, bir nesne yapısındaki elemanlara, onları değiştirmeden yeni işlemler eklemeyi sağlar.
Nesne yapısındaki elemanlardan ayrık olarak, işlemler “Visitor” adlı ayrı sınıflarda tanımlanır ve çalışma zamanında bu işlemler nesneye “ziyaret” edilir.
Visitor deseniyle, bir nesne yapısına yeni işlemler eklemek kolaylaşır; mevcut nesne sınıflarında değişiklik yapmadan yeni fonksiyonellik ekleyebilirsiniz.
Genellikle karmaşık nesne yapılarında (ağaçlar, grafikler, dosya sistemleri gibi) ve birden fazla işlem gerektiren modellerde tercih edilir.

Avantajları:

  • Nesne yapısına yeni işlemler eklemek kolaydır.
  • Nesne sınıflarında değişiklik olmadan yeni fonksiyonlar eklenebilir.
  • Kodun bakımı ve genişletilmesi kolaylaşır.

Dezavantajları:

  • Nesne hiyerarşisinin sabit olması gerekir; sıkça değişiyorsa Visitor kullanmak zorlaşır.
  • Her yeni Visitor için tüm elementlere ekleme yapmak gerekebilir.

Visitor Örnek Senaryo

Bir hayvanat bahçesinde farklı hayvan türleri bulunuyor: fil, zürafa ve maymun.
Hayvanat bahçesi görevlileri, hayvanları ziyaret ederek iki farklı işlem gerçekleştiriyor:

  • Sağlık kontrolü
  • Beslenme kontrolü
    Her hayvan türünde kontrol adımları farklılık gösteriyor:
  • Fil için diş ve kulak kontrolü, özel yem;
  • Zürafa için boyun ve tüy kontrolü, yaprak yem;
  • Maymun için parmak ve tırnak kontrolü, meyve yem.
    Visitor deseniyle, kontrol işlemleri hayvan sınıflarına dokunmadan, ayrı Visitor sınıflarında tanımlanır ve hayvanlar ziyaret edilir.

Visitor C# Kod Örneği

using System;
using System.Collections.Generic;

// Element arayüzü - hayvanlar ziyaretçiyi kabul eder
public interface IAnimal
{
    void Accept(IAnimalVisitor visitor);
}

// Concrete Element: Fil
public class Elephant : IAnimal
{
    public void Accept(IAnimalVisitor visitor)
    {
        visitor.Visit(this);
    }

    public void CheckTeethAndEars()
    {
        Console.WriteLine("Filin dişleri ve kulakları kontrol edildi.");
    }

    public void FeedSpecialFood()
    {
        Console.WriteLine("Fil için özel yem verildi.");
    }
}

// Concrete Element: Zürafa
public class Giraffe : IAnimal
{
    public void Accept(IAnimalVisitor visitor)
    {
        visitor.Visit(this);
    }

    public void CheckNeckAndFur()
    {
        Console.WriteLine("Zürafanın boynu ve tüyleri kontrol edildi.");
    }

    public void FeedLeaves()
    {
        Console.WriteLine("Zürafa için yaprak verildi.");
    }
}

// Concrete Element: Maymun
public class Monkey : IAnimal
{
    public void Accept(IAnimalVisitor visitor)
    {
        visitor.Visit(this);
    }

    public void CheckFingersAndNails()
    {
        Console.WriteLine("Maymunun parmakları ve tırnakları kontrol edildi.");
    }

    public void FeedFruits()
    {
        Console.WriteLine("Maymun için meyve verildi.");
    }
}

// Visitor arayüzü - hayvanlara yapılacak işlemleri tanımlar
public interface IAnimalVisitor
{
    void Visit(Elephant elephant);
    void Visit(Giraffe giraffe);
    void Visit(Monkey monkey);
}

// Concrete Visitor: Sağlık kontrolcüsü
public class HealthInspector : IAnimalVisitor
{
    public void Visit(Elephant elephant)
    {
        Console.WriteLine("Fil sağlık kontrolü yapılıyor.");
        elephant.CheckTeethAndEars();
    }

    public void Visit(Giraffe giraffe)
    {
        Console.WriteLine("Zürafa sağlık kontrolü yapılıyor.");
        giraffe.CheckNeckAndFur();
    }

    public void Visit(Monkey monkey)
    {
        Console.WriteLine("Maymun sağlık kontrolü yapılıyor.");
        monkey.CheckFingersAndNails();
    }
}

// Concrete Visitor: Beslenme kontrolcüsü
public class FeedingInspector : IAnimalVisitor
{
    public void Visit(Elephant elephant)
    {
        Console.WriteLine("Fil beslenme kontrolü yapılıyor.");
        elephant.FeedSpecialFood();
    }

    public void Visit(Giraffe giraffe)
    {
        Console.WriteLine("Zürafa beslenme kontrolü yapılıyor.");
        giraffe.FeedLeaves();
    }

    public void Visit(Monkey monkey)
    {
        Console.WriteLine("Maymun beslenme kontrolü yapılıyor.");
        monkey.FeedFruits();
    }
}

// Kullanım örneği
class Program
{
    static void Main(string[] args)
    {
        List<IAnimal> animals = new List<IAnimal>
        {
            new Elephant(),
            new Giraffe(),
            new Monkey()
        };

        // Sağlık kontrolü yapılıyor
        Console.WriteLine("Sağlık Kontrolü:");
        HealthInspector healthInspector = new HealthInspector();
        foreach (var animal in animals)
        {
            animal.Accept(healthInspector);
        }

        Console.WriteLine("\nBeslenme Kontrolü:");
        // Beslenme kontrolü yapılıyor
        FeedingInspector feedingInspector = new FeedingInspector();
        foreach (var animal in animals)
        {
            animal.Accept(feedingInspector);
        }
    }
}

 

Yorumlar kapalı