Derinlemesine yazılım eğitimleri için kanalımı takip edebilirsiniz...

C# Mediator Design Pattern(Mediator Tasarım Deseni)

Merhaba,

Bu makalemizde Davranışsal Tasarım Kalıplarından(Behavioral Patterns) olan Mediator Tasarım Desenini(Mediator Design Pattern) bol pratiksel senaryolar eşliğinde inceliyor olacağız. İçeriğe başlamadan önce şahsi fikrimi beyan ederek bir girizgâh eylemek isterim ki; bana sorarsanız doğası itibariyle design pattern’lar arasında kullanım sahası açısından oldukça kısır olan lakin son yıllarda MediatR gibi paketlerin popülaritesinin artmasıyla oldukça yaygınlaşan bir tasarım desenini ele alıyor olacağız. Dolayısıyla bu desenin, salt haliyle anlaşılabilmesi ve gerçek senaryolara uyarlanabilmesi oldukça zor ve zahmetli olduğu için ben deniz bir kaç farklı senaryoyla konuyu inceleyecek ve kavranılabilirliği arttırmak için ince dokunuşlarla içeriği destekleyeceğim… Tabi bu süreçte okurken sizlerin ise çay veya kahveyle eşlik etmeniz kafi olacaktır 🙂

Hadi başlayalım…

Mediator Tasarım Kalıbının Amacı Nedir?

Nesneler arasındaki kaotik bağımlılıkları azaltmanıza izin veren davranışsal bir tasarım modelidir.

Hoca, nesneler arasındaki kaotik bağımlılıklardan kastın ne la Allahisen?
Tamam, bunu şöyle örneklendirelim. Müşteri profilleri oluşturmak ve düzenlemek için textboxlar, checkboxlar, buttonlar vs. gibi çeşitli form yapılanmalarının ve bunların kendi aralarında hizalı ve işlevsel bir diyaloğunun olduğunu varsayalım.

C# Mediator Design Pattern(Mediator Tasarım Deseni)

Uygulama geliştikçe kontroller arasındaki ilişkiler daha da kaotik hale gelmektedir.

İşte bu işleminizi yukarıdaki gibi her bir kontrolün birbiriyle temasını sağlayacak şekilde düzenlerseniz kastedilen kaotik bağımlılıklar söz konusudur diyebiliriz.

Bir sınıfın sahip olduğu bağımlılıklar ne kadar azsa, o sınıfı değiştirmek, genişletmek veya yeniden kullanmak o kadar kolay olacaktır.

Mediator tasarım kalıbı ise bu kaotik gidişatı aşağıdaki gibi olması gereken hale getirir ve nesneler arasındaki bağımlılıkları tek bir merkezden yönetimi sağlayarak azaltmamıza imkan tanır.

C# Mediator Design Pattern(Mediator Tasarım Deseni)

Mediator nesnesi aracılığıyla tüm kontroller dolaylı iletişim kurmalıdır.

Böylece nesnelerin birbirleriyle nasıl konuşacaklarını değil, ne konuşacaklarıyla ilgilenen bir tasarım gerçekleştirilmiş olur. İdeal olanda budur!

Mediator, birbirleriyle iletişimde olması gereken nesnelerin iletişimlerinin birebir olmasından ziyade, birbirlerinden bağımsız olacak şekilde olmasını önerir. İki nesne arasında varsa iletişim bunun kesilmesini, yerine bu nesnelerin birbirlerine olacak çağrılarını uygun şekilde yönlendirecek olan özel bir aracı(Mediator) nesnesi üzerinden dolaylı olarak sağlanmasını ister.

Mediator modeli, tek bir aracı(mediator) nesnesi içerisinde çeşitli nesneler arasındaki karmaşık ilişkiler ağını yönetmenize olanak tanır.

Gerçek Dünya Analojisi

C# Mediator Design Pattern(Mediator Tasarım Deseni)

Uçak pilotları, bir sonraki uçağa kimin ineceğine karar verirken birbirleriyle doğrudan konuşmazlar. Tüm iletişim kontrol kulesiyle gerçekleştirilir.

Mediator deyince genellikle uçakların birbirleriyle olan haberleşmesi üzerinden örneklendirme yapılır. Eee haliyle bu da durumu özetleyebilmemizi sağlayan güzel bir argümandır. Lakin bu içeriğimizde pratiksel olarak çok farklı örnekler üzerinden mediator senaryoları ele alacağım. Şimdilik gelin, ilk olarak gerçek dünya analojisinde genelin kabul ettiği uçak metaforunu ele alalım…

Havaalanı iniş pistine yaklaşan yahut kalkışa hazırlanan uçaklar, davranışlarını diğer uçakların durumuna göre belirlemek ve uygunsa eğer aksiyon almak mecburiyetindedirler. İşte hal böyleyken uçaklar birbirleriyle iletişim kurmaktansa kule vasıtasıyla iletişimlerini merkezi olarak sağlamaktadırlar. Neden mi? Çünkü her bir uçak bir diğeriyle iletişim kurmaya kalkarsa eğer inanılmaz bir karışıklık ve başta da söylediğimiz gibi kaotik durum söz konusu olacaktır.

  • Pilot Rıfkı, Arif nerdesin lan ben iniyorum
  • Pilot Arif, olum nereye iniyorsun Hilmi daha kalkmadı
  • Pilot Muiddin, abi kusura bakmayın önce ben geldim
  • Pilot Şuayip, hoş geldin gardaşım
  • Pilot Rıfkı, lan olum nereye önce sen geldin, Hilmi kalksana lan ordan
  • Pilot Muiddin, tamam ne baırıyosun senn önce in
  • Pilot Temel, haçe uşaklar bu Trabzon havalimani nerededur!
  • Pilot Arif, abi yanlış yere gitmişsin, orası Irak m*
  • Pilot Temel, hee dünya yuvarlak ya! tam tersine gidince yine Trabzon çıkayi dediler,
  • Pilot Hilmi, ben kalkıyorum Şuayip abi
  • Pilot Şuayip, banane lan senin kalkıp kalkmadığından 😒
  • vs
  • vs

Anlayacağınız bu tarz muhabbetlerin sürmemesi için uçaklar arası tüm iletişim kule aracılığıyla gerçekleştirilmekte ve yapılacak aksiyona göre karar mekanizması tek elden yönetilmektedir. Haliyle uçaklar kule vasıtasıyla hava trafik kontrolünü sağlayacaklarından dolayı, diğer uçakların ne yaptığı yahut nasıl aksiyon alacağıyla değil bizzat kendi davranışıyla ilgilenmiş olacaktır.

Şimdi konuyu teoride daha iyi anlayabilmek için bir başka örnek üzerinden analoji gerçekleştirelim. Bir sistemdeki kullanıcıların belli başlı gruplara dahil olduğunu düşünelim ve her kullanıcının bir veya daha fazla grubun üyesi olabileceğini ve aynı şekilde her gruba hiç kullanıcı üye olmayacağı gibi bir veya birden fazla üye olabileceğini varsayalım.
C# Mediator Design Pattern(Mediator Tasarım Deseni)Bu ihtiyacı yandaki gibi çözümlediğimiz durumda arapsaçı tabirinin tam karşılığını görmüş olacağız. Düşünsenize, bu şekilde yapılan bir modellemede herhangi bir grubun üzerinde değişiklik ihtiyacı olduğu zaman tüm kullanıcılar istemsizce bu değişiklikten etkilenecektir! Ne kadar kötü!  
 
 
 

C# Mediator Design Pattern(Mediator Tasarım Deseni)Halbuki bu modellemeyi yandaki gibi merkezi bir sınıf üzerinden gerçekleştirip, kullanıcılar ile gruplar arasındaki ilişkiyi soyutladığımızda oldukça avantajlı bir tasarım karşımıza çıkmaktadır. Böyle bir tasarımda yapılacak tüm değişiklikler kolayca gerçekleştirilebilir.

Bir sistemi birçok nesneye bölmek/parçalamak genellikle yeniden kullanılabilirliği(reusability) arttırır. Ancak bu nesneler arasında artan ilişkisel bağlar, tekrar kullanılabilirlik açısından negatif etki gösterir. Mediator nesnesi, nesneler arasındaki tüm ilişkileri kapsülleyerek, iletişim merkezi görevi görür. Etkileşimleri kontrol ve koordine etmekten sorumludur. Ayrıca nesnelerin açıkça birbirlerine referansta bulunmasını engelleyerek gevşek bağlanmayı teşvik eder.

Mediator Anatomisi


Mediator tasarımı yukarıdaki diyagramda da görüldüğü üzere; ‘Mediator‘, ‘Colleague‘, ‘Concrete Mediator‘ ve ‘Concrete Colleague‘ aktörlerinin bir araya gelmesiyle oluşturulur.

Şimdi gelin bu aktörlerin kim olduklarını sorumluluklarıyla beraber inceleyelim;

  • Mediator
    Nesneler arasında aracı işlevini görecek olan Mediator nesnesinin arayüzüdür.
  • Concrete Mediator
    Mediator arayüzünden türemiş olan ve nesneler arası haberleşmeden sorumlu olan somut sınıf. İçerisinde haberleşmelerini sağlayacağı Colleague referanslarını tutar ve gerekli işlevselliği sağlar.
  • Colleague
    Aralarında iletişim/haberleşme sağlayacak olan nesnelerin uygulayacağı arayüzdür. Kendi içlerinde Concrete Mediator referansı tutarlar. Böylece hangi aracı sayesinde diğer nesnelerle dolaylıca iletişim kurduklarını bilirler.
  • Concrete Colleague
    Colleague arayüzünü uygulamış ve birbirleriyle iletşim/haberleşme sağlayacak olan somut nesnelerdir.

Mediator Stratejisinin Uygulanması

Mediator stratejisinin nasıl uygulandığını izah edebilmek için belli başlı senaryolar üzerinden konuyu ele almakta fayda olacağı kanaatindeyim. Bunun için sizlere birbirinden farklı üç senaryo üzerinden anlatım sergilemeye çalışacağım. Şimdi gelin 1. senaryodan konuyu pratikte inceleyelim;

SENARYO 1
Bir bilgisayardaki çeşitli birimler arasındaki etkileşimlerin esas olarak anakart üzerinden yapıldığını hepimiz biliyoruz. Eğer anakart olmasaydı, bu birimlerin etkileşime girebilmesi için birbirleriyle haberleşmesi gerekecekti. Böyle bir durumda ise oldukça karışıklık söz konusu olacağı aşikar. Dolayısıyla her birim sade ve sadece anakartla etkileşime girerek, birimler arasındaki haberleşme sorumluluğunu anakarta yüklemesi gerekmektedir. Böylece ihtiyaç doğrultusunda hangi birimin kiminle ne şekilde etkileşime gireceğini ve nasıl çalışacağını sadece anakart bilecektir.

Bu senaryoda, CDDriver’a takılan bir CD’de ki verilerin CPU’ya aktarılıp ardından ses ve ekran kartlarına ayrıştırılması süreçlerini Mediator tasarım deseniyle modelliyor olacağız. CDDriver ile CD’den alınan/okunan verileri anakart CPU’ya gönderip, işletecektir. Bunun için CDDriver’ın anakarta durumunun değiştiğini bildirmesi yeterli olacaktır. Aynı şekilde CPU aldığı verileri video ve ses verilerine bölüştürerek anakart vasıtasıyla ekran ve ses kartlarına gönderecektir. Yine bu işlem için CPU’nun da anakarta durumunun değiştiğini bildirmesi gerekecektir. Nihai olarak, anakart vasıtasıyla CD’de ki veriler ekran ve ses kartlarına ulaşmış ve işlenmiş olacaktır.

Aktörler & Roller
Anakart Mediator
CDDriver Colleague
Ses Kartı Colleague
Ekran Kartı Colleague
SENARYO 1 ÇÖZÜM
  • Adım 1
    Her şeyden önce Mediator arayüzünü oluşturarak başlayalım.

        /// <summary>
        /// Mediator Arayüzü
        /// </summary>
        public interface IAnakart
        {
            void Degistir(Birim birim);
        }
    

    Dikkat ederseniz bu arayüz, aralarında iletişim kurulacak olan nesnelerin(Birim) birbirleriyle haberleşmesini sağlayacak olan ‘Degistir’ imzasını barındırmaktadır. Ne zaman herhangi, bir Colleague tarafından bu ‘Degistir’ fonksiyonu tetiklensin, o zaman Mediator diğer nesneyle etkileşim kuracak ve tetikleyecektir.

    Ayrıca Mediator arayüzü örnekte olduğu gibi interface olabileceği gibi abstract class olarakta tanımlanabilmektedir.

  • Adım 2
    Hemen akabinde Colleague arayüzünü oluşturmamız yerinde olacaktır.

        /// <summary>
        /// Colleague Arayüzü
        /// </summary>
        public abstract class Birim
        {
            protected IAnakart _anakart;
            public Birim(IAnakart anakart)
            {
                _anakart = anakart;
            }
        }
    

    Colleague arayüzü yukarıda olduğu gibi içerisinde Mediator arayüzünden bir referans tutmalıdır.

  • Adım 3
    Sırada Concrete Colleague nesnelerinin oluşturması gerekmektedir.

    CDDriver : CD’de ki (farazi)dataları okuyarak anakartı bilgilendiriyor.

        /// <summary>
        /// Concrete Colleague
        /// </summary>
        public class CDDriver : Birim
        {
            public CDDriver(IAnakart anakart) : base(anakart)
            {
            }
            string _cdData;
            public string CDData => _cdData;
            public void CDOku()
            {
                _cdData = "görüntü1,görüntü2,görüntü3*ses1,ses2,ses3";
                _anakart.Degistir(this);
            }
        }
    

    CPU : CD’den gelen verileri ses ve video datalarına ayrıştırarak anakart vasıtasıyla ekran kartı ve ses kartına gönderiyor.

        /// <summary>
        /// Concrete Colleague
        /// </summary>
        public class CPU : Birim
        {
            public CPU(IAnakart anakart) : base(anakart)
            {
            }
            string _videoData, _sesData;
            public string VideoData => _videoData;
            public string SesData => _sesData;
            public void DataIsle(string cdData)
            {
                string[] array = cdData.Split("*");
                _videoData = array[0]; //görüntü değerleri video data olarak alınıyor.
                _sesData = array[1]; //ses değerleri ses data olarak alınıyor.
                _anakart.Degistir(this);
            }
        }
    

    Ekran Karti : CPU’dan gelen video datalarını işliyor.

        /// <summary>
        /// Concrete Colleague
        /// </summary>
        public class EkranKarti : Birim
        {
            public EkranKarti(IAnakart anakart) : base(anakart)
            {
            }
            public void GorselVer(string videoData)
            {
                string[] datas = videoData.Split(",");
                foreach (string data in datas)
                    Console.WriteLine($"Gelen görüntü : {data}");
            }
        }
    

    Ses Karti : CPU’dan gelen ses datalarını işliyor.

        /// <summary>
        /// Concrete Colleague
        /// </summary>
        public class SesKarti : Birim
        {
            public SesKarti(IAnakart anakart) : base(anakart)
            {
            }
            public void SesVer(string sesData)
            {
                string[] datas = sesData.Split(",");
                foreach (string data in datas)
                    Console.WriteLine($"Gelen ses : {data}");
            }
        }
    
  • Adım 4
    Şimdi tüm bu Colleague nesnelerinin olması gereken iletişimlerini davranışsal olarak tasarlayan ve somut aracı sorumluluğunu üstlenecek olan Concrete Mediator sınıfını oluşturalım.

        /// <summary>
        /// Concrete Mediator
        /// </summary>
        public class Anakart : IAnakart
        {
            CDDriver _cdDriver;
            CPU _cpu;
            EkranKarti _ekranKarti;
            SesKarti _sesKarti;
    
            public CDDriver CDDriver { set => _cdDriver = value; }
            public CPU CPU { set => _cpu = value; }
            public EkranKarti EkranKarti { set => _ekranKarti = value; }
            public SesKarti SesKarti { set => _sesKarti = value; }
    
            public void Degistir(Birim birim)
            {
                if (birim is CDDriver)
                {
                    string cdData = _cdDriver.CDData;
                    _cpu.DataIsle(cdData);
                }
                else if (birim is CPU)
                {
                    string videoData = _cpu.VideoData, sesData = _cpu.SesData;
                    _ekranKarti.GorselVer(videoData);
                    _sesKarti.SesVer(sesData);
                }
            }
        }
    

    Mediator sınıfı aralarındaki haberleşmeyi sağlayacağı tüm Colleague sınıflarını bilmek ister. Dolayısıyla bu sınıfın yapısına göz atarsanız, 6 ile 9. satır aralığında sistemdeki tüm birimlerin referansları tanımlanmıştır. 11 ile 14. satır aralığında ise bu nesneleri set edebileceğimiz property’ler tanımlanmıştır. Tabi bunlar istek yahut ihtiyaç doğrultusunda metot olarak da tanımlanabilirler.

    ‘Degistir’ metoduna nazar eylerseniz eğer artık hangi birim tarafından bir bildiri geliyorsa içeride ayırt edilmekte ve yapılacak iş/davranış duruma göre işlenmektedir. Dolayısıyla bu sınıf nesneler arasındaki haberleşme görevini böylece yerine getirmektedir.

  • Adım 5
    Sonuç olarak uygulanan Mediator desenini aşağıdaki gibi kullanarak, test edelim.

    Görüldüğü üzere CDDriver ile CD’den okunan verilerin sırayla CPU -> Ekran Kartı -> Ses Kartı silsilesi şeklinde akışını Mediator(Anakart) nesnesi sayesinde tek elden yönetebilmiş bulunmaktayız.

İşte bu kadar… Şimdi 2. senaryoya geçebiliriz.

SENARYO 2
Şehirler arası nakliye firmalarını düşünelim. Örneğin; Iğdır’dan Edirne’ye taşınacak bir ailenin eşyalarını nakliye edebilmek için üç farklı şehirde aktarmalı lojistik firmaları görev alıyor olsun.

Iğdır’dan Sıvas’a “X Firma”sı,
Sivas’tan Ankara’ya “Y Firma”sı ve
Ankara’dan Edirne’ye “Z Firması” nakliye operasyonunda bulunuyor olsun.

Aktörler & Roller
Nakliye Mediator
X Firma Colleague
Y Firma Colleague
Z Firma Colleague
SENARYO 2 ÇÖZÜM
  • Adım 1
    Mediator arayüzünü oluşturalım.

        /// <summary>
        /// Mediator Arayüzü
        /// </summary>
        public interface INakliye
        {
            void MaliDevret(Firma firma);
        }
    
  • Adım 2
    Colleague arayüzünü oluşturalım.

        /// <summary>
        /// Colleague Arayüzü
        /// </summary>
        public abstract class Firma
        {
            protected INakliye _nakliye;
    
            protected Firma(INakliye nakliye)
            {
                _nakliye = nakliye;
            }
            public abstract void NakliyeyeBasla();
        }
    

    Bu senaryoda tanımlanan Colleague arayüzünü incelerseniz eğer içerisinde abstract ‘NakliyeyeBasla’ fonksiyonu barındırmaktadır. Çünkü tüm firmalar aynı işi yapacak, malı alacak ve nakliye işlemine devam edecektir. Dolayısıyla bunun için tüm Colleague sınıflarında ortak bir imza olması yerinde bir tasarım olacaktır. Yani buradan da görüldüğü üzere bir tasarımı uygularken riayet edilecek olan sadece genel stratejidir. İhtiyaç doğrultusunda bu ve buna benzer taktiksel yapılanmalar tabi ki de yapılmalıdır, yapılacaktır.

  • Adım 3
    Firmalara karşılık gelecek olan somut Colleague sınıflarımızı oluşturalım.

        /// <summary>
        /// Concrete Colleague
        /// </summary>
        public class XFirma : Firma
        {
            public XFirma(INakliye nakliye) : base(nakliye)
            {
            }
            public override void NakliyeyeBasla()
            {
                Console.WriteLine("Iğdır'dan eşyalar yüklendi.");
                _nakliye.MaliDevret(this);
            }
        }
    
        /// <summary>
        /// Concrete Colleague
        /// </summary>
        public class YFirma : Firma
        {
            public YFirma(INakliye nakliye) : base(nakliye)
            {
            }
            public override void NakliyeyeBasla()
            {
                Console.WriteLine("Sivas'tan eşyalar yüklendi.");
                _nakliye.MaliDevret(this);
            }
        }
    
        /// <summary>
        /// Concrete Colleague
        /// </summary>
        public class ZFirma : Firma
        {
            public ZFirma(INakliye nakliye) : base(nakliye)
            {
            }
            public override void NakliyeyeBasla()
            {
                Console.WriteLine("Ankara'dan eşyalar yüklendi.");
                _nakliye.MaliDevret(this);
            }
        }
    
  • Adım 4
    Artık ilgili nesnelerin aralarındaki iletişimi sağlayacak ve davranışlarını yönetecek olan somut Mediator nesnesini tasarlayalım.

        /// <summary>
        /// Concrete Mediator
        /// </summary>
        public class Nakliye : INakliye
        {
            XFirma _xfirma;
            YFirma _yfirma;
            ZFirma _zfirma;
            public XFirma XFirma { set => _xfirma = value; }
            public YFirma YFirma { set => _yfirma = value; }
            public ZFirma ZFirma { set => _zfirma = value; }
            public void MaliDevret(Firma firma)
            {
                if (firma is XFirma)
                {
                    Console.WriteLine("Eşyalar Sivas'ta tekrar nakledilmek üzere indirildi.\n");
                    _yfirma.NakliyeyeBasla();
                }
                else if (firma is YFirma)
                {
                    Console.WriteLine("Eşyalar Ankara'da tekrar nakledilmek üzere indirildi.\n");
                    _zfirma.NakliyeyeBasla();
                }
                else
                    Console.WriteLine("Eşyalar Edirne'ye vardı çok şükür...");
            }
        }
    
  • Adım 5
    Sonuç olarak yapıyı kullanarak çalışmamızı yapalım ve test edelim.
    C# Mediator Design Pattern(Mediator Tasarım Deseni)

Evet… İşte böyle 🙂 Hadi gelin, hazır ısınmışken bir sonraki senaryoyu inceleyelim.

SENARYO 3
Bu senaryomuzda bir konut alım satım sürecindeki para transferini ele alıyor olacağız. Yapılan antlaşma neticesinde; alıcı, emlakçı ve ev sahibi arasında yapılacak olan para transferi şu şekilde gerçekleşecektir;
Alıcı, eğer konutun fiyatı 100.000 TL’den büyükse parayı satıcıya gönderecek ve ardından satıcı emlakçıya %3’lük komisyonu cebinden verecek.. Yok eğer konutun fiyatı 100.000 TL’den küçükse alıcı, emlakçının %10’luk komisyonunu cebinden verecek ve biryandan da konutun tam parasını satıcıya gönderecektir.

Normal şartlarda bu operasyonu nesneler arasında birebir iletişim kurarak gerçekleştirmeye çalışmak Suriye meselesinden hallice olacaktır. İşte burada Mediator deseni biçilmiş kaftandır.

Aktörler & Roller
Transfer Mediator
Alıcı Colleague
Emlakçı Colleague
Satıcı Colleague
SENARYO 3 ÇÖZÜM
  • Adım 1
    Her zamanki gibi Mediator arayüzü oluşturarak başlıyoruz.

        /// <summary>
        /// Mediator Arayüzü
        /// </summary>
        public interface ITransfer
        {
            void ParaGonder(Kisi kisi, int tutar);
        }
    
  • Adım 2
    Ardından soyut Colleague sınıfını tasarlıyoruz.

        /// <summary>
        /// Concrete Arayüzü
        /// </summary>
        public abstract class Kisi
        {
            protected ITransfer _transfer;
            protected Kisi(ITransfer transfer)
            {
                _transfer = transfer;
            }
        }
    
  • Adım 3
    Concrete Colleague sınıflarımızı oluşturalım.

        /// <summary>
        /// Concrete Colleague
        /// </summary>
        public class Alici : Kisi
        {
            public Alici(ITransfer transfer) : base(transfer)
            {
            }
    
            public void KonutParasiOde(int tutar)
            {
                Console.WriteLine("Alıcı : Ödeme yapılmıştır.");
                _transfer.ParaGonder(this, tutar);
            }
        }
    
        /// <summary>
        /// Concrete Colleague
        /// </summary>
        public class Emlakci : Kisi
        {
            public Emlakci(ITransfer transfer) : base(transfer)
            {
            }
    
            public void KomisyonAl(int tutar)
                => Console.WriteLine($"Emlakçı : {tutar} TL kadar komisyon alınmıştır.");
        }
    
        /// <summary>
        /// Concrete Colleague
        /// </summary>
        public class Satici : Kisi
        {
            public Satici(ITransfer transfer) : base(transfer)
            {
            }
    
            public void ParaAl(int tutar)
            {
                Console.WriteLine($"Satıcı : {tutar} TL kadar konut bedeli alınmıştır.");
                _transfer.ParaGonder(this, tutar);
            }
        }
    
  • Adım 4
    Ve ardından tüm bu nesneleri organize edecek olan somut Mediator nesnesini oluşturalım.

        /// <summary>
        /// Concrete Mediator
        /// </summary>
        public class Transfer : ITransfer
        {
            Alici alici;
            Emlakci emlakci;
            Satici satici;
    
            public Alici Alici { set => alici = value; }
            public Emlakci Emlakci { set => emlakci = value; }
            public Satici Satici { set => satici = value; }
    
            public void ParaGonder(Kisi kisi, int tutar)
            {
                if (kisi is Alici && tutar > 100000)
                    satici.ParaAl(tutar);
                else if (kisi is Alici && tutar <= 100000)
                {
                    emlakci.KomisyonAl(tutar * 10 / 100);
                    satici.ParaAl(tutar);
                }
                else if (kisi is Satici && tutar > 100000)
                    emlakci.KomisyonAl(tutar * 3 / 100);
            }
        }
    
  • Adım 5
    Test edelim.
    C# Mediator Design Pattern(Mediator Tasarım Deseni)

İşte bu kadar…

Haliyle Mediator tasarım desenini pratik açıdan yeterince ele aldık kanaatindeyim.

Şimdi gelin konuyu teoride toparlayarak makalemizi noktalayalım.

Mediator Nerelerde Uygulanmalı?

  • Bazı sınıfların ya da sınıf gruplarının, bir sınıfa sıkıca bağlı oldukları durumlarda davranışlarını değiştirmek inanılmaz zor olacağı için Mediator deseni kullanılabilir.
  • Bir sınıfın, farklı sınıflara bağımlı olması durumundan dolayı başka yerlerde yeniden kullanılabilirliği olumsuz etkilenecektir. İşte böyle bir durumda Mediator deseni kullanılabilir.

Mediator Deseninin Artıları ve Eksileri Nelerdir?

Artıları Eksileri
Nesneler arasındaki ilişkiler tek bir yerden(Mediator) yönetileceği için her nesne kendi sorumluluğuna odaklanabilir. Böylece Tek Sorumluluk Prensibi/Single Responsibility Principle sağlanmış olunmakta ve ilgili sınıf tasarımının anlaşılabilirliği artarak, bakımı kolaylaşmaktadır. Zamanla Mediator sınıfı, çok fazla iş/sorumluluk yükleneceği ve haddinden fazla davranış yöneteceği için ister istemez bir Tanrı Nesnesine(God Object) dönüşebilir.
Mevcut nesneleri değiştirmeye gerek kalmaksızın uygulamaya yeni nesneler dahil edilebilir. Böylece değişime kapalı lakin gelişime açık olacak şekilde bir tasarım söz konusu olduğundan dolayı Açık Kapalı Prensibi/Open Closed Principle desteklenmektedir. Mediator deseni aşırı merkezileşme sağlar. Haliyle Mediator sorumluluğunu üstlenecek olan sınıf oldukça yönetilmesi ve sürdürülmesi zor bir sınıf olacaktır.
Geliştirilen bir kodda çeşitli nesneler arasındaki ilişkiyi ve somut bağlantıları Mediator sayesinde azaltabilir ve böylece yönetilebilirlik açısından daha kaliteli kodlar geliştirebilirsiniz.
Her bir nesne esasında bağımsız bir davranış sergileyeceğinden dolayı tek başına da rahatlıkla kullanılabilmektedir.
Nesneler arasında Gevşek Bağlılık(Loose Coupling) söz konusu olmaktadır.

Veritabanındaki many to many ilişkiler Cross Table ile sağlanmaktadır. Aynı mantıkla class’lar da da bu sorumluluğu Mediator sınıfı üstlenmektedir.

Mediator İle Benzer Desenler

Mediator ile Facade Benzerliği
Fark ettiniz mi bilmiyorum ama Facade Design Pattern(Facade Tasarım Deseni) ile Mediator oldukça birbirlerine benzemektedirler. Her ikiside birbirlerine sıkı sıkıya bağlı olan birçok nesne arasındaki diyaloğu düzenlemeye çalışmaktadırlar.

İkisinin arasındaki temel fark;

Facade; nesnelerin alt sistemlerine basitleştirilmiş bir arayüz sağlar. Alt sistemin kendisi Facade arayüzünden habersizdir. Alt sistem içerisinde tüm nesneler doğrudan birbirleriyle iletişim kurabilirler.
Mediator; nesneler(sistemler) arasında iletişim merkezidir. Nesneler yalnızca Mediator nesnesini bilir ve birbirleriyle doğrudan iletişim kurmazlar.

Mediator ile Observer Benzerliği
Mediator ile bir diğer benzerlik gösteren desen ise Observer Design Pattern(Observer Tasarım Deseni)dir. Hatta çoğu durumda aralarındaki farkı anlamak neredeyse imkansız olabileceği için birbirlerinin yerine kullanılabilmektedirler.

Bu iki desen arasındaki ince nüens aşağıdaki gibidir;

Mediator’ın birincil amacı nesneler arasındaki karşılıklı bağımlılıkları ortadan kaldırmaktır. Bunun için bu nesneleri tek bir Mediator nesneye bağımlı hale getiririz.
Observer’ın amacı ise bazı nesneler ile onlara bağlı olan diğer nesneler arasında dinamik tek yönlü bağlantıyı kurmaktır.

Evet… Böylece bu makalenin de sonuna gelmiş bulunmaktayız. Umarım sizin açınızdan aydınlatıcı bilgiler sunabilmişimdir. Sıkılmadan, usanmadan bu satırlara kadar ulaşabilen değerli okuyucularıma en içten teşekkürlerimi sunarım.

İlgilenenlerin faydalanması dileğiyle…
Sonraki yazılarımda görüşmek üzere…
İyi çalışmalar…

Örnek senaryo kodlarını indirebilmek için buraya tıklayınız.

Bunlar da hoşunuza gidebilir...

8 Cevaplar

  1. Ferhat dedi ki:

    Pilot muhabbetleri çok yaratıcı ve nokta atışı bir örnek olmuş :)) Elinize emeğinize sağlık çok güzel bir anlatım olmuş

  2. Tolga dedi ki:

    Bir makaleyi İngilizce’den Türkçe’ye çevirmek de insanlara büyük katkı sağlıyor. Benim de anlamakta zorlandığım ve Türkçe’ye çevrilmesini arzuladığım kitaplar var. Ancak çevirileri yaparken kaynak belirtmek bence çok önemli.

    https://refactoring.guru/design-patterns/mediator

  3. Uğur dedi ki:

    Çok iyi bir paylaşım olmuş teşekkürler.

  4. Gülşah dedi ki:

    Çok teşekkürler harika bir yazı :))

  5. Bahad dedi ki:

    Cok muthis bir izzah etme yeteneyine sahibsiziniz, tesekkur ederim

  1. 06 Mart 2021

    […] bilgi edinebilmeniz için konuya dair detaylı ve bol örnekli bir şekilde kaleme aldığım Mediator Design Pattern başlıklı makalemi referans göstermek istiyorum. Yinede kısaca ne olduğunu özetlememiz […]

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir