Fluent Interface Nedir? Nasıl Kullanılır?

Merhaba,

Sizlere aslen bir Design Pattern olan Fluent Interface yapısından bahsedeceğim. Fluent, “akıcı” anlamına gelen bir kelimedir. Yani anlayacağınız kod yazarken belli başlı durumlarda daha net ve akıcı kod yazmamızı sağlayan bir tasarım desenini ele alıyor olacağız.

Asıl mevzu, bir nesnenin metodlarının aynı türde bir nesne geri döndürdüğü bir modeldir. Tabi ki de bu nesne, o anki “this” keywordü ile ifade ettiğimiz nesnenin ta kendisi olacaktır.

Şimdi aşağıda Fluent Interface Design Pattern’i uygulamadığımız sınıfımız mevcuttur.

    class Asker
    {
        public void Kos() => Console.WriteLine("Asker düz koşuyor...");
        public void SagaKos() => Console.WriteLine("Asker sağa koşuyor...");
        public void SolaKos() => Console.WriteLine("Asker sola koşuyor...");
        public void Dur() => Console.WriteLine("Asker duruyor...");
        public void GeriGel() => Console.WriteLine("Asker geri geliyor...");
    }

Asker sınıfını kullanırsak eğer;

        static void Main(string[] args)
        {
            Asker asker = new Asker();
            asker.Kos();
            asker.SagaKos();
            asker.Kos();
            asker.Dur();
            asker.GeriGel();
            asker.SolaKos();
            asker.Kos();
            Console.Read();
        }

gördüğünüz gibi referans üzerinde oldukça maliyetli bir kod yazmaktayız.

Şimdi bu tarz bir durumda Fluent Interface’ini uygularsak konuyu daha net açıklayacağına inanmaktayım.
Fluent Interface Nedir? Nasıl Kullanılır?
Class diyagramında gördüğünüz gibi IDavranislar Interface’i tarafından Asker sınıfına uygulanan tüm metodlar geriye IDavranislar nesnesini döndürmektedir. Yani Method Chaining – Zincirleme Yöntemi dediğimiz manevrayı gerçekleştirmektedirler.(Bir sınıf içerisindeki herhangi bir metodun işlevi sonucunda aynı sınıf tipinde nesne geri döndürmesine Method Chaining(Zincirleme Yöntemi) denir.)

    interface IDavranislar
    {
        IDavranislar Kos();
        IDavranislar SagaKos();
        IDavranislar SolaKos();
        IDavranislar Dur();
        IDavranislar GeriGel();
    }
    class Asker : IDavranislar
    {
        public IDavranislar Kos()
        {
            Console.WriteLine("Asker düz koşuyor...");
            return this;
        }
        public IDavranislar SagaKos()
        {
            Console.WriteLine("Asker sağa koşuyor...");
            return this;
        }
        public IDavranislar SolaKos()
        {
            Console.WriteLine("Asker sola koşuyor...");
            return this;
        }
        public IDavranislar Dur()
        {
            Console.WriteLine("Asker duruyor...");
            return this;
        }
        public IDavranislar GeriGel()
        {
            Console.WriteLine("Asker geri geliyor...");
            return this;
        }
    }

Gördüğünüz gibi her bir metod geriye IDavranislar arayüzünü döndürmektedir. Ee haliyle Asker sınıfımızı da IDavranislar Interface’inden türettiğimiz için “this” keywordü ile geri o anki nesneyi döndürebilmekteyiz.

Bu tasarının kullanımını aşağıdaki gibi gerçekleştirebilmekteyiz.

        static void Main(string[] args)
        {
            Asker asker = new Asker();
            asker.Kos()
                .SagaKos()
                .Kos()
                .Dur()
                .GeriGel()
                .SolaKos()
                .Kos();
            Console.Read();
        }

Çıktı olarak iki kullanım tarzıda aynı sonucu verecektir.

Fluent Interface gördüğünüz gibi bu tarz uygulamalarda pratiklik kazandırsada diğer yandan semantik olarak sakıncalıdır. Çünkü ilgili yapı bir API olarak size sunulmuş olabilir. Haliyle nasıl çalıştığını teknik olarak bilmiyorsanız eğer size her defasında var olan instanceı mı yoksa yenisini mi döndüğünü direkt olarak bilemezsiniz. Haliyle yazılan kodun etkisini anlamlandırabilmek için ek olarak çaba sarf etmeniz gerekecektir. Bunun yanında, yazdığınız kodu başkasının okuyabilmesi ve ne yapmaya çalıştığınızı anlaması güçleşebilir.

Şimdi Fluent Interface Design Pattern’nini genişletmek maksatlı bir örnek daha yapalım.
Fluent Interface Nedir? Nasıl Kullanılır?

    interface ITank
    {
        ITank SetTankAdi(string _TankAdi);
        ITank SetSeviye(Seviye _Seviye);
        ITank SetKoordinat(Koordinat _Koordinat);
        ITank SetTankResmi(string _TankResmi);
        ITank SetToplamPuan(double _ToplamPuan);
        void Yazdir();
    }
    enum Seviye
    {
        CokKolay,
        Kolay,
        Normal,
        Zor,
        CokZor
    }
    struct Koordinat
    {
        public int X { get; set; }
        public int Y { get; set; }
    }
    class Tank : ITank
    {
        string _TankAdi;
        Seviye _Seviye;
        Koordinat _Koordinat;
        string _TankResmi;
        double _ToplamPuan;

        public void Yazdir()
        {
            Console.WriteLine(
                $@"Tank Adı : {_TankAdi}
                   Seviye : {_Seviye}
                   Koordinat : {_Koordinat.X } x {_Koordinat.Y}
                   Tank Resmi : {_TankResmi}
                   Toplam Puan : {_ToplamPuan}");
        }

        public ITank SetTankAdi(string _TankAdi)
        {
            this._TankAdi = _TankAdi;
            return this;
        }

        public ITank SetSeviye(Seviye _Seviye)
        {
            this._Seviye = _Seviye;
            return this;
        }

        public ITank SetKoordinat(Koordinat _Koordinat)
        {
            this._Koordinat = _Koordinat;
            return this;
        }

        public ITank SetTankResmi(string _TankResmi)
        {
            this._TankResmi = _TankResmi;
            return this;
        }

        public ITank SetToplamPuan(double _ToplamPuan)
        {
            this._ToplamPuan = _ToplamPuan;
            return this;
        }
    }

Gördüğünüz gibi bu örneğimizde de metodlar aracılığıyla ilgili nesne üzerindeki fieldlara veriler set edilmektedir.

        static void Main(string[] args)
        {
            Tank tank = new Tank();
            tank.SetSeviye(Seviye.Kolay)
                .SetKoordinat(new Koordinat() { X = 3, Y = 5 })
                .SetTankAdi("Gençay")
                .SetTankResmi("gencay.png")
                .SetToplamPuan(145)
                .Yazdir();

            Console.Read();
        }

Çıktı olarak aşağıdaki ekran görüntüsü alınmaktadır.
Fluent Interface Nedir? Nasıl Kullanılır?

Dilerseniz elimizdeki örnekleri birazdaha genişletelim.
Fluent Interface Nedir? Nasıl Kullanılır?

    interface IYetenek
    {
        IYetenek SetYetenek(Yetenek _Yetenek);
        IYetenek Yazdir(Yetenek _Yetenek);
    }
    class Yetenek
    {
        public string _Yetenek { get; set; }
        public void Yazdir()
        {
            Console.WriteLine(_Yetenek);
        }
    }
    class Tank : IYetenek
    {
        List<Yetenek> Yetenekler = new List<Yetenek>();

        public IYetenek SetYetenek(Yetenek _Yetenek)
        {
            if (!Yetenekler.Contains(_Yetenek))
            {
                Yetenekler.Add(_Yetenek);
            }

            return this;
        }

        public IYetenek Yazdir(Yetenek _Yetenek)
        {
            var Yetenek = Yetenekler.Find(y => y == _Yetenek);
            Yetenek.Yazdir();
            return this;
        }
    }

Bu örneğin kullanımı,

        static void Main(string[] args)
        {
            Yetenek Hucum = new Yetenek { _Yetenek = "Hücummmmmma kalkıyoruz, Allah AlllllAh" };
            Yetenek Ates = new Yetenek { _Yetenek = "Ates edildi..." };
            Yetenek Kac = new Yetenek { _Yetenek = "Kaç kaççç" };
            Yetenek FuzeKalkanlariDevredemi = new Yetenek { _Yetenek = "hee devrede devrede" };


            Tank tank = new Tank();
            tank.SetYetenek(Hucum)
                .SetYetenek(Ates)
                .SetYetenek(Kac)
                .Yazdir(Kac)
                .Yazdir(Hucum)
                .SetYetenek(FuzeKalkanlariDevredemi)
                .Yazdir(Ates)
                .Yazdir(FuzeKalkanlariDevredemi);
            Console.Read();
        }

şeklindedir. Gördüğünüz gibi bu tarz bir tasarıda, farklı adımlarda farklı kabiliyetler ilgili nesneye entegre edilebilmektedir.
Fluent Interface Nedir? Nasıl Kullanılır?

Şimdi son kez yukarıdaki örneğin akabinde Extension metodlarla Fluent Interface desenini ele alalım.
Fluent Interface Nedir? Nasıl Kullanılır?

    interface IYetenek
    {
        IYetenek SetYetenek(Yetenek _Yetenek);
        IYetenek Yazdir(Yetenek _Yetenek);
    }
    class Yetenek
    {
        public string _Yetenek { get; set; }
        public void Yazdir(string _TankAdi)
        {
            Console.WriteLine(_TankAdi + " " + _Yetenek);
        }
    }
    class Tank : IYetenek
    {
        string _TankAdi;
        List<Yetenek> Yetenekler = new List<Yetenek>();

        public IYetenek SetYetenek(Yetenek _Yetenek)
        {
            if (!Yetenekler.Contains(_Yetenek))
            {
                Yetenekler.Add(_Yetenek);
            }

            return this;
        }

        public Tank(string _TankAdi)
        {
            this._TankAdi = _TankAdi;
        }

        public IYetenek Yazdir(Yetenek _Yetenek)
        {
            var Yetenek = Yetenekler.Find(y => y == _Yetenek);
            Yetenek.Yazdir(_TankAdi);
            return this;
        }
    }

Extension metodlarımızı barındıran Extension ismini verdiğim sınıfımız;

    static class Extension
    {
        static public IEnumerable<Tank> SetYetenek(this IEnumerable<Tank> list, Yetenek _Yetenek)
        {
            list.ToList().ForEach(y => y.SetYetenek(_Yetenek));
            return list;
        }
        static public IEnumerable<Tank> Yazdir(this IEnumerable<Tank> list, Yetenek _Yetenek)
        {
            list.ToList().ForEach(y => y.Yazdir(_Yetenek));
            return list;
        }
    }

Kullanımı;

        static void Main(string[] args)
        {
            Yetenek Hucum = new Yetenek { _Yetenek = "Hücummmmmma kalkıyoruz, Allah AlllllAh" };
            Yetenek Ates = new Yetenek { _Yetenek = "Ates edildi..." };
            Yetenek Kac = new Yetenek { _Yetenek = "Kaç kaççç" };
            Yetenek FuzeKalkanlariDevredemi = new Yetenek { _Yetenek = "hee devrede devrede" };

            Tank t1 = new Tank("Gençay Tank");
            Tank t2 = new Tank("Göktürk Tank");
            Tank t3 = new Tank("Tank Tank");
            Tank t4 = new Tank("Çorum Tank");
            Tank t5 = new Tank("Tank");
            List<Tank> Tanklar = new List<Tank> { t1, t2, t3, t4, t5 };
            Tanklar.SetYetenek(Hucum)
                .SetYetenek(FuzeKalkanlariDevredemi)
                .Yazdir(Hucum)
                .SetYetenek(Ates)
                .SetYetenek(Kac)
                .Yazdir(Ates)
                .Yazdir(Kac);
            Console.Read();
        }

Örneğimizde gördüğünüz gibi IEnumerable<Tank> tipinden koleksiyon üzerinde işlemler yapmamızı sağlayan iki adet Extension metod oluşturduk. Kullanımından da göreceğiniz gibi elimizdeki birden fazla mevcut tank üzerine bu Extension metodlar aracılığıyla yeni yetenekler entegre edebilmekte ve var olanı yazdırabilmekteyiz.

Fluent Interface Nedir? Nasıl Kullanılır?

Okuduğunuz için teşekkür ederim…

Sonraki yazılarımda görüşmek üzere…

İyi çalışmalar…

Kaynak : http://www.buraksenyurt.com/post/Fluent-Interface-Nedir.aspx

Bunlar da hoşunuza gidebilir...

Bir cevap yazın

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

*

Copy Protected by Chetan's WP-Copyprotect.