Takip et

Giriş: Bileşenler Arası Kaosu Düzenlemenin Bir Yolu Var mı?

Padrão Mediator: Karmaşık Sistemleri Nasıl Sadeleştiririz?

Modern yazılım geliştirme, bileşenler arası iletişim ağları büyüdükçe karmaşık bir yapıya bürünebilir. Peki, uygulamalarınızdaki bileşenlerin birbirleriyle doğrudan konuşması yerine, tüm bu etkileşimi düzenleyen merkezi bir nokta olsaydı ne olurdu? Padrão Mediator, işte tam da bu soruna zarif bir çözüm sunarak, sistemlerinizi daha yönetilebilir ve esnek hale getirmenizi sağlıyor.

Bir yazılım projesi geliştirmeye başladığımızda, başlangıçta her şey oldukça düzenli ve anlaşılır görünebilir. Ancak zamanla, yeni özellikler eklendikçe ve farklı bileşenler birbirleriyle etkileşime girmeye başladıkça işler karışmaya başlar. Bir düğmenin tıklanması başka bir arayüz elemanını güncellerken, o güncellenen eleman da farklı bir veri parçasını etkileyebilir. Kısa sürede, her bir bileşenin diğer birçok bileşenle doğrudan bağlantı kurduğu, “spagetti kod” olarak tabir edilen, içinden çıkılmaz bir bağımlılık ağı oluşur.

Bu karmaşık iletişim ağı, beraberinde birçok sorunu getirir. Öncelikle, yeni bir özellik eklemek veya mevcut bir davranışı değiştirmek oldukça zorlaşır; çünkü bir bileşende yapılan değişiklik, sistemin tahmin edilemeyen başka noktalarında yan etkilere yol açabilir. Bu durum, hata ayıklama süreçlerini uzatır ve yazılımın bakım maliyetini önemli ölçüde artırır. Ayrıca, her bileşenin diğerlerinin iç işleyişi hakkında bilgi sahibi olması gerektiğinden, bileşenler arası bağımlılık (coupling) artar ve bu da kodun yeniden kullanılabilirliğini düşürür.

Peki, bu kaosu düzenlemenin ve bileşenler arası bağımlılığı azaltmanın bir yolu yok mu? Elbette var. Tasarım desenleri, yazılım geliştiricilerin sık karşılaştığı bu tür problemlere test edilmiş ve kanıtlanmış çözümler sunar. Bu makalede ele alacağımız Mediator deseni, tam da bu noktada devreye girer. Mediator, bileşenlerin birbirleriyle doğrudan iletişim kurmasını engelleyerek, tüm etkileşimin merkezi bir nesne üzerinden gerçekleşmesini sağlar. Bu sayede, her bileşen sadece Mediator’ı tanır ve onunla iletişim kurar, diğer bileşenlerin varlığından bile haberdar olmaz. Böylece, sistemdeki her bir parça daha izole hale gelir, daha kolay test edilebilir ve çok daha esnek bir yapıya kavuşur.

Padrão Mediator Nedir ve Temel Kavramları Nelerdir?

Padrão Mediator, Gang of Four (GoF) tasarım desenleri arasında yer alan davranışsal (behavioral) bir desendir. Temel amacı, bir nesne kümesinin birbiriyle doğrudan etkileşimini kapsülleyerek, nesneler arasındaki bağlantıları gevşetmektir. Yani, bu desen, nesnelerin doğrudan birbirine bağımlı olmak yerine, tüm iletişimi merkezi bir “arabulucu” (mediator) nesne üzerinden gerçekleştirmesini sağlar.

Bu deseni bir hava trafik kontrolörüne benzetebiliriz. Uçaklar (bileşenler) birbirleriyle doğrudan konuşmak yerine, hava trafik kontrolörüne (mediator) bilgi verir ve ondan talimat alırlar. Kontrolör, tüm uçakların konumunu ve rotasını bilir ve olası çarpışmaları önlemek için gerekli talimatları verir. Bu sayede, her pilot sadece kontrolörle etkileşim kurar ve diğer uçakların karmaşık rotalarıyla uğraşmak zorunda kalmaz. Benzer şekilde, yazılım bileşenleri de Mediator ile etkileşim kurarak diğer bileşenlerin iç detaylarına bağımlı olmaktan kurtulur.

Mediator deseninde anahtar rol oynayan iki temel kavram vardır:

  1. Mediator (Arabulucu): Bu, tüm iletişim mantığını içeren merkezi nesnedir. Bileşenlerden gelen mesajları alır, bu mesajları yorumlar ve ilgili bileşenlere yönlendirir. Bileşenler arasındaki koordinasyonu ve iş akışını yönetir. Genellikle bir arayüz (IMediator) ile tanımlanır ve farklı concrete uygulamaları olabilir.
  2. Colleague (Meslektaş/İş Arkadaşı): Bunlar, Mediator ile etkileşim kuran bireysel bileşenlerdir. Her bir Colleague, Mediator’ı bilir ve onunla iletişim kurar ancak diğer Colleague‘lerin varlığından veya işleyişinden haberdar değildir. Kendi iş mantıklarını yürütürken, diğer bileşenlerle etkileşime ihtiyaç duyduklarında bu isteği Mediator’a iletirler.

Bu yapının getirdiği en büyük fayda, bağımlılıkların azalmasıdır. Bir bileşende değişiklik yapıldığında, diğer bileşenlerin kodu üzerinde minimum etki oluşur; çünkü onlar hala aynı Mediator arayüzü ile konuşmaya devam ederler. Ayrıca, sistemin iş akışı ve iletişim mantığı merkezi bir yerde toplandığı için, kodu anlamak ve bakımını yapmak çok daha kolay hale gelir. Özellikle büyük ve karmaşık sistemlerde, bu tür bir merkezileşme, geliştirme verimliliğini ve yazılımın kalitesini önemli ölçüde artırabilir.

Uzman İpucu: Mediator deseni, özellikle çok sayıda bileşenin birbiriyle etkileşime girdiği ve bu etkileşimlerin karmaşık iş kuralları içerdiği durumlarda parlar. UI bileşenleri, oyunlardaki karakter etkileşimleri veya bir iş akışı motoru gibi senaryolar için idealdir.

Bir Senaryo Üzerinden Mediator Deseni Nasıl Çalışır? Gerçek Dünya Örneği

Şimdi gelin, Padrão Mediator‘ın gerçek bir senaryoda nasıl çalıştığını daha somut bir örnek üzerinden inceleyelim: Bir kullanıcı arayüzü formu. Diyelim ki bir ürün sipariş formu geliştirmemiz gerekiyor. Bu formda şu bileşenler var:

  • Ürün Seçimi (Dropdown): Kullanıcı buradan bir ürün seçer.
  • Adet Girişi (TextBox): Seçilen üründen kaç adet alınacağını belirler.
  • Fiyat Gösterimi (Label): Seçilen ürün ve adete göre toplam fiyatı gösterir.
  • Stok Kontrolü (CheckBox): “Stokta Var” durumunu gösterir, adet girişini etkileyebilir.
  • Sipariş Onayla (Button): Tüm bilgiler doğruysa aktif olur.

Mediator deseni olmasaydı, bu bileşenler arasındaki etkileşim şöyle olabilirdi:

  • Ürün Seçimi değiştiğinde: Fiyat Gösterimi’ni güncelle, Adet Girişi’ni sıfırla, Stok Kontrolü’nü tetikle.
  • Adet Girişi değiştiğinde: Fiyat Gösterimi’ni güncelle, Sipariş Onayla butonunun aktifliğini Stok Kontrolü ve Adet miktarına göre ayarla.
  • Stok Kontrolü değiştiğinde: Adet Girişi’nin maksimum değerini ayarla, Sipariş Onayla butonunun aktifliğini etkile.
  • Fiyat Gösterimi değiştiğinde: (Kendisi pasif, diğerleri tarafından güncellenir).
  • Sipariş Onayla tıklandığında: Tüm alanların geçerliliğini kontrol et.

Gördüğünüz gibi, her bir bileşen diğer birkaç bileşeni doğrudan tanımak ve onların metodlarını çağırmak zorunda kalıyor. Bu da yukarıda bahsettiğimiz “spagetti kod” durumuna yol açıyor. Yeni bir bileşen eklemek veya mevcut bir bileşenin davranışını değiştirmek, domino etkisi yaratma riski taşıyor.

Problem Açıklama
Yüksek Bağımlılık Her UI bileşeni, diğerlerini doğrudan tanır ve çağırır.
Bakım Zorluğu Bir bileşendeki değişiklik, diğerlerinin de kodunu etkileyebilir.
Düşük Yeniden Kullanılabilirlik Bileşenler birbirine sıkıca bağlı olduğu için başka bir formda kullanmak zordur.

Şimdi ise Mediator deseni ile bu senaryoyu nasıl ele alacağımıza bakalım:

Tüm bu bileşenler (Colleague‘ler) kendi başlarına bağımsızdır ve yalnızca bir FormMediator‘ı tanırlar. Bir bileşen bir olay tetiklediğinde (örneğin, “Ürün Seçimi değişti”), bu olayı doğrudan FormMediator‘a bildirir. FormMediator ise, tüm bileşenlerin mevcut durumunu ve birbirleriyle olan ilişkilerini bilen merkezi nesnedir. Olayı aldıktan sonra, ilgili iş kurallarına göre hangi diğer bileşenlerin güncellenmesi gerektiğine karar verir ve bu bileşenlerin ilgili metotlarını çağırır.

Örneğin:

  • Ürün Seçimi değiştiğinde, FormMediator.UrunSecildi(urunKodu) metodunu çağırır.
  • FormMediator bu çağrıyı aldığında:
    1. FiyatGosterimi bileşeninin FiyatiGuncelle(yeniFiyat) metodunu çağırır.
    2. AdetGiris bileşeninin AdetiSifirla() metodunu çağırır.
    3. StokKontrolu bileşenine yeni ürünün stok durumunu iletir (StokDurumunuAyarla(varMi)).
  • Adet Girişi değiştiğinde, FormMediator.AdetDegisti(adet) metodunu çağırır.
  • FormMediator bu çağrıyı aldığında:
    1. FiyatGosterimi bileşeninin FiyatiGuncelle(yeniFiyat) metodunu çağırır (adet ve ürün fiyatına göre).
    2. SiparisOnayla butonunun AktifligiAyarla(gecerliMi) metodunu çağırır (adet ve stok durumuna göre).

Bu yaklaşım sayesinde, her UI bileşeni sadece kendi işini yapar ve olayları Mediator’a bildirir. Diğer bileşenlerin varlığından bile haberi olmaz. Böylece, sistemdeki bileşenler gevşek bağlı (loosely coupled) hale gelir, bu da bakımı ve yeniden kullanılabilirliği büyük ölçüde iyileştirir.

Mediator Deseni Adım Adım Nasıl Uygulanır? Kod Örnekleriyle

Şimdi Padrão Mediator’ı C# dilinde adım adım nasıl uygulayacağımızı inceleyelim. Önceki senaryomuzdaki form uygulamasını temel alacağız.

Adım 1: Mediator Arayüzünü Tanımlama (IMediator)

Öncelikle, tüm bileşenlerin iletişim kuracağı arayüzü tanımlayalım. Bu arayüz, Mediator’ın gerçekleştirmesi gereken tüm iletişim metotlarını içerecektir.


public interface IFormMediator
{
    void UrunSecimiDegisti(Urun selectedProduct);
    void AdetDegisti(int quantity);
    void StokDurumuGuncellendi(bool isInStock);
    void SiparisOnayla();
    void RegisterComponent(IFormComponent component);
}
    

Burada RegisterComponent metodu, Mediator'ın form üzerindeki tüm bileşenleri tanımasını ve onlara referans tutmasını sağlar. Diğer metotlar ise bileşenlerden gelen olayları temsil eder.

Adım 2: Colleague Arayüzünü ve Temel Colleague Sınıfını Oluşturma (IFormComponent ve ComponentBase)

Her bir form bileşeninin (Colleague) Mediator'ı tanımasını ve onunla etkileşim kurmasını sağlayacak bir temel sınıf oluşturmak iyi bir uygulamadır. Bu sayede her bileşenin SetMediator gibi ortak bir metodu olur.


public interface IFormComponent
{
    void SetMediator(IFormMediator mediator);
    string GetName(); // Sadece debugging için eklenebilir
}

public abstract class ComponentBase : IFormComponent
{
    protected IFormMediator _mediator;
    protected string _name;

    public ComponentBase(string name)
    {
        _name = name;
    }

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

    public string GetName()
    {
        return _name;
    }
}
    

Adım 3: Concrete Colleague Sınıflarını Oluşturma

Şimdi formumuzdaki her bir bileşen için Concrete Colleague sınıflarını tanımlayalım. Bu sınıflar, kendi olaylarını Mediator'a bildirecek ve Mediator'dan gelen komutlara yanıt vereceklerdir.


// Basit bir Ürün sınıfı
public class Urun
{
    public string Ad { get; set; }
    public decimal BirimFiyat { get; set; }
    public bool StoktaVar { get; set; }
}

// Ürün Seçimi Dropdown Bileşeni
public class UrunSecimiComponent : ComponentBase
{
    public UrunSecimiComponent() : base("UrunSecimi") { }

    private Urun _selectedProduct;
    public Urun SelectedProduct
    {
        get { return _selectedProduct; }
        set
        {
            if (_selectedProduct != value)
            {
                _selectedProduct = value;
                Console.WriteLine($"[{_name}] Ürün '{_selectedProduct?.Ad}' olarak seçildi.");
                _mediator?.UrunSecimiDegisti(_selectedProduct);
            }
        }
    }
}

// Adet Girişi TextBox Bileşeni
public class AdetGirisComponent : ComponentBase
{
    public AdetGirisComponent() : base("AdetGiris") { }

    private int _quantity;
    public int Quantity
    {
        get { return _quantity; }
        set
        {
            if (_quantity != value)
            {
                _quantity = value;
                Console.WriteLine($"[{_name}] Adet: {_quantity}");
                _mediator?.AdetDegisti(_quantity);
            }
        }
    }

    public void SetQuantity(int q)
    {
        Console.WriteLine($"[{_name}] Adet harici olarak ayarlandı: {q}");
        _quantity = q;
    }
}

// Fiyat Gösterimi Label Bileşeni
public class FiyatGosterimComponent : ComponentBase
{
    public FiyatGosterimComponent() : base("FiyatGosterim") { }

    private decimal _totalPrice;
    public decimal TotalPrice
    {
        get { return _totalPrice; }
        private set
        {
            _totalPrice = value;
            Console.WriteLine($"[{_name}] Toplam Fiyat: {_totalPrice:C}");
        }
    }

    public void UpdatePrice(decimal price)
    {
        TotalPrice = price;
    }
}

// Stok Kontrolü CheckBox Bileşeni
public class StokKontrolComponent : ComponentBase
{
    public StokKontrolComponent() : base("StokKontrol") { }

    private bool _isInStock;
    public bool IsInStock
    {
        get { return _isInStock; }
        private set
        {
            _isInStock = value;
            Console.WriteLine($"[{_name}] Stok Durumu: {(_isInStock ? "VAR" : "YOK")}");
            _mediator?.StokDurumuGuncellendi(_isInStock);
        }
    }

    public void SetStockStatus(bool status)
    {
        IsInStock = status;
    }
}

// Sipariş Onayla Butonu Bileşeni
public class SiparisOnaylaComponent : ComponentBase
{
    public SiparisOnaylaComponent() : base("SiparisOnayla") { }

    private bool _isEnabled;
    public bool IsEnabled
    {
        get { return _isEnabled; }
        private set
        {
            _isEnabled = value;
            Console.WriteLine($"[{_name}] Buton Durumu: {(_isEnabled ? "AKTİF" : "PASİF")}");
        }
    }

    public void SetEnabled(bool enable)
    {
        IsEnabled = enable;
    }

    public void Click()
    {
        if (IsEnabled)
        {
            Console.WriteLine($"[{_name}] Sipariş Onaylandı!");
            _mediator?.SiparisOnayla();
        }
        else
        {
            Console.WriteLine($"[{_name}] Buton pasif, sipariş onaylanamadı.");
        }
    }
}
    

Adım 4: Concrete Mediator Sınıfını Oluşturma

Son olarak, tüm bu bileşenlerin iletişimini yönetecek olan FormMediator sınıfını implement edelim. Bu sınıf, tüm Colleague'lere referans tutar ve gelen olaylara göre hangi Colleague'lerin hangi metotlarını çağıracağına karar verir.


public class ConcreteFormMediator : IFormMediator
{
    private UrunSecimiComponent _urunSecimi;
    private AdetGirisComponent _adetGiris;
    private FiyatGosterimComponent _fiyatGosterim;
    private StokKontrolComponent _stokKontrol;
    private SiparisOnaylaComponent _siparisOnayla;

    // Örnek ürünler
    private readonly List _urunler = new List
    {
        new Urun { Ad = "Laptop", BirimFiyat = 10000, StoktaVar = true },
        new Urun { Ad = "Mouse", BirimFiyat = 250, StoktaVar = true },
        new Urun { Ad = "Klavye", BirimFiyat = 700, StoktaVar = false }
    };

    public void RegisterComponent(IFormComponent component)
    {
        if (component is UrunSecimiComponent urunSecimi) _urunSecimi = urunSecimi;
        else if (component is AdetGirisComponent adetGiris) _adetGiris = adetGiris;
        else if (component is FiyatGosterimComponent fiyatGosterim) _fiyatGosterim = fiyatGosterim;
        else if (component is StokKontrolComponent stokKontrol) _stokKontrol = stokKontrol;
        else if (component is SiparisOnaylaComponent siparisOnayla) _siparisOnayla = siparisOnayla;
        
        component.SetMediator(this); // Mediator'ı bileşene ata
    }

    //region Olay İşleyiciler
    public void UrunSecimiDegisti(Urun selectedProduct)
    {
        Console.WriteLine("[Mediator] Ürün seçimi değişti.");
        if (selectedProduct == null)
        {
            _fiyatGosterim.UpdatePrice(0);
            _adetGiris.SetQuantity(0);
            _stokKontrol.SetStockStatus(false);
            _siparisOnayla.SetEnabled(false);
            return;
        }

        // Fiyatı güncelle
        _fiyatGosterim.UpdatePrice(selectedProduct.BirimFiyat * _adetGiris.Quantity);
        
        // Adeti sıfırla (genellikle yeni ürün seçildiğinde adet sıfırlanır)
        _adetGiris.SetQuantity(1); // Varsayılan 1 adet olarak ayarla
        
        // Stok durumunu güncelle
        _stokKontrol.SetStockStatus(selectedProduct.StoktaVar);

        // Sipariş onayla butonunu güncelle
        UpdateSiparisOnaylaButtonStatus();
    }

    public void AdetDegisti(int quantity)
    {
        Console.WriteLine("[Mediator] Adet değişti.");
        if (_urunSecimi.SelectedProduct == null)
        {
            _fiyatGosterim.UpdatePrice(0);
            _siparisOnayla.SetEnabled(false);
            return;
        }

        // Fiyatı güncelle
        _fiyatGosterim.UpdatePrice(_urunSecimi.SelectedProduct.BirimFiyat * quantity);
        
        // Sipariş onayla butonunu güncelle
        UpdateSiparisOnaylaButtonStatus();
    }

    public void StokDurumuGuncellendi(bool isInStock)
    {
        Console.WriteLine("[Mediator] Stok durumu güncellendi.");
        // Eğer stokta yoksa, adet 0 yapılır ve buton pasif hale gelir.
        if (!isInStock)
        {
            _adetGiris.SetQuantity(0);
        }
        UpdateSiparisOnaylaButtonStatus();
    }

    public void SiparisOnayla()
    {
        Console.WriteLine("[Mediator] Sipariş onaylama isteği alındı.");
        // Burada gerçek sipariş işleme mantığı olabilir.
        // Örneğin, bir servis çağrısı yapmak.
        Console.WriteLine("Sipariş işleme alındı: " +
                          $"{_urunSecimi.SelectedProduct.Ad} - {_adetGiris.Quantity} adet " +
                          $"Toplam: {_fiyatGosterim.TotalPrice:C}");
    }
    //endregion

    // Yardımcı metot: Sipariş Onayla butonunun durumunu belirle
    private void UpdateSiparisOnaylaButtonStatus()
    {
        bool enableButton = _urunSecimi.SelectedProduct != null &&
                            _adetGiris.Quantity > 0 &&
                            _stokKontrol.IsInStock;
        _siparisOnayla.SetEnabled(enableButton);
    }

    // Dışarıdan ürün listesini döndürme (örneğin UI'ın dropdown'ı doldurması için)
    public List GetAvailableProducts()
    {
        return _urunler;
    }
}
    

Adım 5: Uygulamayı Çalıştırma

Şimdi tüm bu parçaları bir araya getirelim ve nasıl çalıştığını görelim.


public class Program
{
    public static void Main(string[] args)
    {
        Console.WriteLine("Mediator Deseni Demoo Başlatılıyor...\n");

        // 1. Mediator ve Colleague'leri oluştur
        IFormMediator mediator = new ConcreteFormMediator();

        UrunSecimiComponent urunSecimi = new UrunSecimiComponent();
        AdetGirisComponent adetGiris = new AdetGirisComponent();
        FiyatGosterimComponent fiyatGosterim = new FiyatGosterimComponent();
        StokKontrolComponent stokKontrol = new StokKontrolComponent();
        SiparisOnaylaComponent siparisOnayla = new SiparisOnaylaComponent();

        // 2. Colleague'leri Mediator'a kaydet (ve Mediator'ı Colleague'lere ata)
        ((ConcreteFormMediator)mediator).RegisterComponent(urunSecimi);
        ((ConcreteFormMediator)mediator).RegisterComponent(adetGiris);
        ((ConcreteFormMediator)mediator).RegisterComponent(fiyatGosterim);
        ((ConcreteFormMediator)mediator).RegisterComponent(stokKontrol);
        ((ConcreteFormMediator)mediator).RegisterComponent(siparisOnayla);

        Console.WriteLine("--- Senaryo 1: Laptop Seçimi ve Adet Değişikliği ---");
        Urun laptop = ((ConcreteFormMediator)mediator).GetAvailableProducts().First(p => p.Ad == "Laptop");
        urunSecimi.SelectedProduct = laptop; // Kullanıcı Laptop seçti
        // Mediator otomatik olarak adet girişini 1'e ayarlayacak ve fiyatı güncelleyecek.
        // Ayrıca stok durumunu da ayarlayacak ve butonu aktif edecek.

        adetGiris.Quantity = 2; // Kullanıcı adeti 2 olarak değiştirdi
        // Mediator, fiyatı ve butonun aktifliğini tekrar ayarlayacak.

        siparisOnayla.Click(); // Kullanıcı siparişi onayladı

        Console.WriteLine("\n--- Senaryo 2: Klavye Seçimi (Stokta Yok) ---");
        Urun klavye = ((ConcreteFormMediator)mediator).GetAvailableProducts().First(p => p.Ad == "Klavye");
        urunSecimi.SelectedProduct = klavye; // Kullanıcı Klavye seçti
        // Mediator, adet girişini sıfırlayacak, stok durumunu YOK olarak ayarlayacak ve butonu pasif yapacak.

        adetGiris.Quantity = 5; // Kullanıcı stokta olmayan üründen adet girmeye çalıştı
        // Mediator, adet 5 olsa da stokta olmadığı için butonu aktif etmeyecek.

        siparisOnayla.Click(); // Buton pasif olduğu için sipariş onaylanmayacak.

        Console.WriteLine("\nDemo Sonlandı.");
        Console.ReadKey();
    }
}
    

Yukarıdaki kod örneklerinde, her bir bileşen kendi durumunu yönetiyor ve değişiklikleri Mediator'a bildiriyor. Mediator ise bu değişikliklere göre diğer bileşenlerin durumunu güncelliyor. Böylece, bileşenler birbirlerinin iç işleyişinden haberdar olmak zorunda kalmıyor, bu da kodu çok daha modüler ve anlaşılır hale getiriyor.

Mediator'ı Kullanırken Dikkat Edilmesi Gereken İpuçları ve En İyi Uygulamalar Nelerdir?

Padrão Mediator, doğru kullanıldığında sistem karmaşıklığını önemli ölçüde azaltabilirken, yanlış kullanıldığında yeni sorunlara yol açabilir. İşte Mediator'ı uygularken göz önünde bulundurmanız gereken bazı ipuçları ve en iyi uygulamalar:

  • Ne Zaman Kullanmalısınız?
    • Çok sayıda nesne karmaşık yollarla birbirine bağlı olduğunda ve bu bağımlılıkları azaltmak istediğinizde.
    • Nesneler arası iletişim mantığını merkezi bir yerde toplamak ve yeniden kullanılabilirliği artırmak istediğinizde.
    • Nesneler arasındaki etkileşimde değişiklik yapmanın, diğer nesneler üzerinde büyük bir etki yaratmasını istemediğinizde. Özellikle kullanıcı arayüzü (UI) kontrolleri arasındaki etkileşimlerde veya iş akışı yönetimi gibi süreçlerde çok faydalıdır.
  • Potansiyel Dezavantajlara Dikkat! "God Object" Riski: Mediator deseninin en büyük dezavantajı, Mediator nesnesinin zamanla "Tanrı Nesne" (God Object) haline gelme riskidir. Yani, çok fazla sorumluluk üstlenip çok fazla iş mantığı içermesi durumunda, kendisi bir darboğaz ve bakım kabusu haline gelebilir. Bu durum, deseni kullanmanın temel faydasını (karmaşıklığı azaltma) ortadan kaldırır. Bu nedenle, Mediator'ın sadece iletişimi koordine etmeye odaklanmasına ve iş mantığını mümkün olduğunca Colleague'lerde bırakmaya çalışın.
  • Soyutlama Seviyesi: IMediator ve IColleague (veya ComponentBase) gibi arayüzler ve soyut sınıflar kullanmak, esnekliği artırır. Bu sayede, farklı ConcreteMediator uygulamaları veya farklı ConcreteColleague tipleri kolayca sisteme dahil edilebilir.
  • İletişim Yöntemi: Mediator ile Colleague'ler arasındaki iletişim tek yönlü (Colleague'den Mediator'a) veya çift yönlü olabilir. Genellikle, Colleague'ler olayları Mediator'a bildirir ve Mediator da gerekli Colleague'lerin metotlarını çağırarak onları günceller. Bu tek yönlü (olay tabanlı) yaklaşım genellikle daha temizdir.
  • Kayıt (Registration) Mekanizması: Mediator'ın tüm Colleague'leri tanıması gerekir. Bu, bir yapılandırıcı (constructor) aracılığıyla veya bizim örneğimizdeki gibi bir RegisterComponent metodu ile yapılabilir. Büyük ve dinamik sistemlerde, bu kayıt işlemini otomatikleştirmek için bir Dependency Injection (DI) çatısı kullanmak faydalı olabilir.
  • Test Edilebilirlik: Mediator deseni, bileşenleri birbirinden ayırdığı için birim testlerini (unit test) kolaylaştırır. Her bir Colleague, Mediator'ı mock'layarak kendi başına test edilebilir. Aynı şekilde, Mediator da Colleague'leri mock'layarak kendi iletişim mantığını test edebilir.
  • Alternatiflerle Kıyaslama: Her zaman bir tasarım deseni kullanmak zorunda değilsiniz. Basit iletişim senaryolarında, doğrudan iletişim veya Observer deseni daha uygun olabilir. Mediator, özellikle iletişim ağının karmaşıklaştığı durumlarda değerlidir.
Gelişmiş İpucu: Çok büyük ve karmaşık Mediator'ları bölmek için "Hierarchical Mediators" (Hiyerarşik Arabulucular) kullanmayı düşünebilirsiniz. Her alt-Mediator, belirli bir alt sistemin iletişimini yönetirken, üst-Mediator daha genel koordinasyonu sağlar.

Padrão Mediator'ın Avantajları ve Dezavantajları Nelerdir?

Her tasarım deseninde olduğu gibi, Padrão Mediator'ın da hem güçlü yönleri hem de bazı zayıflıkları bulunmaktadır. Bu dengeyi anlamak, deseni ne zaman ve nasıl kullanacağınıza karar verirken kritik öneme sahiptir.

Avantajları:

  • Gevşek Bağlılık (Loose Coupling): En büyük faydası budur. Bileşenler birbirleri yerine yalnızca Mediator ile etkileşime girer. Bu, bileşenlerin birbirlerinin iç işleyişleri hakkında bilgi sahibi olmasını engeller ve bağımlılıkları önemli ölçüde azaltır. Sonuç olarak, bir bileşende yapılan değişiklik diğerlerini doğrudan etkilemez.
  • Merkezi Kontrol ve İş Akışı Yönetimi: Tüm iletişim mantığı ve iş akışı, merkezi bir Mediator nesnesinde toplanır. Bu, sistemin davranışını tek bir yerden anlamayı, değiştirmeyi ve yönetmeyi kolaylaştırır.
  • Yeniden Kullanılabilirlik (Reusability): Bileşenler diğer bileşenlerden bağımsız hale geldiği için, onları farklı bağlamlarda veya farklı uygulamalarda yeniden kullanmak çok daha kolaydır.
  • Test Edilebilirlik (Testability): Gevşek bağlılık sayesinde, bireysel bileşenleri veya Mediator'ı diğerlerinden izole ederek birim testleri yazmak daha kolaydır.
  • Sistem Karmaşıklığını Azaltma: Nesneler arası direkt iletişim ağının karmaşıklığını, merkezi bir Mediator'a yönlendirerek genel sistem karmaşıklığını azaltır. Her bileşenin diğer N-1 bileşenle iletişim kurması yerine, sadece 1 Mediator ile iletişim kurması yeterlidir.
  • Esneklik ve Genişletilebilirlik: Yeni bir bileşen eklemek veya mevcut bir bileşenin iletişim kuralını değiştirmek, genellikle sadece Mediator üzerinde küçük değişiklikler yapmayı gerektirir, bu da sistemi daha esnek hale getirir.

Dezavantajları:

  • "God Object" Riski: Daha önce de belirtildiği gibi, Mediator'ın tüm iş mantığını ve etkileşimleri kendi içinde toplaması, onu devasa ve yönetilmesi zor bir "Tanrı Nesnesi" haline getirebilir. Bu, deseni kullanmanın amacına aykırıdır ve yeni bir karmaşıklık kaynağı yaratır.
  • Tek Hata Noktası (Single Point of Failure): Tüm iletişim Mediator üzerinden geçtiği için, eğer Mediator'da bir hata olursa, bu tüm sistemi etkileyebilir. Ayrıca, Mediator'ın performansı, sistemin genel performansını etkileyen bir darboğaz haline gelebilir.
  • Başlangıç Karmaşıklığı: Çok basit senaryolarda, doğrudan iletişim kurmak daha kolay ve hızlı olabilir. Mediator desenini uygulamak, ek arayüzler ve sınıflar gerektirdiğinden, başlangıçta ekstra soyutlama ve kod karmaşıklığı getirebilir.
  • Sıcaklık Arttıkça Bakım Zorluğu (Over-complex Mediator): Eğer Mediator çok fazla sorumluluk üstlenirse ve çok sayıda Colleague ile karmaşık etkileşimleri yönetmek zorunda kalırsa, kendi içinde anlaşılması ve bakımı zor bir hale gelebilir.

Sonuç olarak, Padrão Mediator güçlü bir araçtır ancak her zaman en iyi çözüm değildir. Özellikle büyük, karmaşık ve çok sayıda bileşenin birbirini etkilemesi gereken sistemlerdeki bağımlılıkları azaltmak ve merkezi kontrol sağlamak için son derece faydalıdır. Ancak, onu dikkatli bir şekilde tasarlamak ve "God Object" tuzağına düşmemek kritik öneme sahiptir.

Sonuç: Sistemlerinizi Daha Okunabilir ve Yönetilebilir Hale Getirin

Bu makalede Padrão Mediator'ı derinlemesine inceledik ve karmaşık sistemlerde bileşenler arası iletişimi nasıl sadeleştirdiğini, bağımlılıkları nasıl azalttığını ve genel sistem mimarisini nasıl daha esnek hale getirdiğini gördük. Hava trafik kontrolöründen bir UI formu senaryosuna kadar farklı örneklerle, bu güçlü davranışsal tasarım deseninin mantığını ve uygulama adımlarını ele aldık. Unutmayın ki, her tasarım deseni gibi Mediator da belirli sorunları çözmek için tasarlanmıştır ve her duruma uygun olmayabilir.

Ancak, özellikle büyük ölçekli ve çok sayıda etkileşimli bileşen içeren yazılım projelerinde, Mediator deseni geliştiricilere büyük kolaylıklar sağlayabilir. Bileşenler arasındaki direkt bağımlılıkları ortadan kaldırarak kodun okunabilirliğini artırır, bakımı kolaylaştırır ve yeni özelliklerin entegrasyonunu hızlandırır. Dolayısıyla, bir sonraki projenizde sisteminizdeki bileşenler arası iletişim karmaşıklaşmaya başladığında, Padrão Mediator'ı bir çözüm olarak değerlendirmeniz, yazılımınızın uzun vadeli sağlığı için akıllıca bir yatırım olacaktır.

Sıkça Sorulan Sorular (SSS)

S: Padrão Mediator ve Observer deseni arasındaki fark nedir?

C: Her iki desen de nesneler arası iletişimi kolaylaştırır ancak farklı yaklaşımlara sahiptir. Observer deseninde, "Subject" (konu) kendi "Observer" (gözlemci) listesini tutar ve bir olay olduğunda tüm gözlemcilerini bilgilendirir. Observerlar birbirleri hakkında bilgi sahibi değildir. Mediator deseninde ise, tüm iletişim merkezi bir Mediator nesnesi üzerinden geçer. Bileşenler (Colleague'ler) birbirlerini değil, sadece Mediator'ı tanır. Mediator, hangi bileşenin ne zaman hangi bileşenle etkileşime gireceğine karar veren bir orkestratör görevi görür.

S: Mediator deseni performansı etkiler mi?

C: Teorik olarak, ek bir soyutlama katmanı ve arabulucu çağrısı olduğu için doğrudan iletişime göre çok küçük bir performans ek yükü olabilir. Ancak modern donanım ve yazılım optimizasyonları göz önüne alındığında, bu ek yük çoğu uygulama için göz ardı edilebilir düzeydedir. Mediator'ın getirdiği bakım kolaylığı ve esneklik faydaları, genellikle bu küçük performans maliyetinden daha ağır basar.

S: Mediator desenini hangi programlama dillerinde kullanabilirim?

C: Mediator deseni bir kavramsal modeldir ve herhangi bir nesne yönelimli programlama dilinde (OOP) uygulanabilir. C#, Java, Python, JavaScript, C++, Ruby gibi dillerde yaygın olarak kullanılır. Dildeki arayüz, soyut sınıf ve sınıf kavramları deseni uygulamak için yeterlidir.

S: Ne zaman Mediator deseni kullanmaktan kaçınmalıyım?

C: Eğer sisteminizdeki bileşenler arası iletişim çok basitse, sadece iki bileşen doğrudan etkileşime giriyorsa veya iletişim mantığı çok doğrusal ve az sayıda kural içeriyorsa, Mediator desenini kullanmak gereksiz bir soyutlama katmanı ve karmaşıklık getirebilir. Bu durumlarda doğrudan iletişim, Observer deseni veya komut deseni gibi daha basit çözümler daha uygun olabilir. Ayrıca, Mediator'ın bir "God Object" haline gelme riski varsa, alternatif çözümler veya Mediator'ı daha küçük parçalara bölme stratejileri düşünülmelidir.

Yorumlar
İçeriği beğendiniz mi? Bir tartışma başlatın veya görüşlerinizi paylaşın.
Yorum Yaz

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Gönder

E-posta Bülteni
Yazılım Topluluğuna Katılın
En son güncellemeleri, yaratıcı ipuçlarını ve özel kaynakları doğrudan e-posta kutunuza alın. Tasarım ve inovasyonun geleceğini birlikte keşfedelim.
Mobil sürümden çık