25 Kasım 2017 Cumartesi

C# interface Kavramı (Arabirimler)


Merhaba,

Size bu yazıda C#'ta önemli bir konu olan interface yapısından bahsedeceğim. Türkçe'deki diğer adı arabirimler. Bu yazıda interface terimini kullanacağım.

interface nedir?

interface, işlevselliği olmayan ama işlevselliği tarif eden, bir işin nasıl yapılacağını değil de ne yapılacağını tarif eden mantıksal bir özelliktir. Türkçe adından da anlaşılacağı üzere bir arabirimdir. abstract classların bir adım ileriye götürülmüş halidir. abstract classları hatırlayalım (abstract classların detayı için tıklayın) bazı metotların  gövdesi olmakla birlikte abstract metotların gövdesi yoktu. kalıtım yoluyla aktarıldığı sınıflarda uygulanmak zorundaydı. interface'de ise bütün metotlar gövdesizdir. Yani bir interfacei kalıtım yoluyla devralan bir sınıf interfacein bütün metotlarını uygulamak zorundadır. Bu, hem kod garantisi sağlar hem de hataları minimize eder. Hem de büyük projelerde bir prototip ortaya koymamızı sağlar. 

Bazen bir uygulamanın ana şablonlarını ortaya koymak istersiniz. Neler yapılacağını belirlersiniz fakat detaylarına girmezsiniz. Detayları size bağlı çalışanlarınız yapacaktır. İşte bu tam olarak interface tanımına uygundur. Alın size gerçek hayattan interfacelere örnek.. Diyelim ki siz bir işletmenin patronusunuz ve kakaolu kek yapacaksınız. Kakaolu kek için ne lazım? Kakao, un, şeker.. Bu üç malzeme olmazsa olmazdır. O zaman projenizde ne olmalı? KakaoGetir(), UnGetir(), SekerGetir()... Siz patsonsunuz ya, tabiki de siz markete gidip bunları temin etmeyeceksiniz. Hepsi de farklı marketlerde bulunabilir. Nihayetinde bu görevleri siz değil, size bağlı kişiler yapacaktır. Ahmet! Sen kakao getir. Mehmet! Sen de un getir. Veli e oğlum sen de şeker getir. Ahmet der ki: patron kakaoyu nerden alayım nasıl getireyim? Patron der ki ben karışmam getir ama nerden getirirsen getir, ister arabayla git ister yürüyerek ama istersen şu yandaki X marketinin ürünleri güzeldir ordan alabilirsin ama yeter ki bana getir der. Ahmet de allem eder kallem eder gider kakaoyu getirir. X marketinde bulamayıp Y marketine gider getirir ama getirir. Mehmet de yürüyerek yandaki bakkaldan un alır gelir. Veli de motora atlayıp kestirme yolunu en iyi bildiği marketten şeker alır gelir. Sonuç olarak kakao, un ve şeker, kekin olmazsa olmazıdır bu üç malzeme tamamlanmazsa kek ortaya çıkmayacaktır. Ahmet, Mehmet ve Veli üçü de görevini layıkıyla yerine getirip kekin yapılmasında büyük katkıları olmuştur.

Gelelim, kod olarak interfacelerin tanımına.. Genel olarak interface tanımı şu şekilde yapılır

interface ad {

          tip1 metot1 (parametre listesi);
          tip2 metot2 (parametre listesi);
          .
          .
          .
}

Dikkat edin, metotların gövdesi yoktur. Sadece dönüş tipi, metot adı ve metot parametrelerine sahiptir. Asıl iş, bu interfacei devralacak classlarda yapılacaktır. metotlar kapalı olarak publictir. Mantıklı olarak düşünüldüğünde, bir interfacei devralacak herhangi bir class içindeki tüm metotları uygulamak zorunda olduğuna göre bütün metotlara erişmek zorundadır. Dolayısıyla metotların neden kapalı olarak public olduğu anlaşılabilir.

interfaceler veri üyeleri içermezler, yapılandırıcı ve yok edicilere de (constructor, destructor) sahip olamazlar, ayrıca hiçbir üyesi static olamaz. Çünkü interfaceler classlar gibi nesne üretmezler sadece arabirimlerdir. interfaceleri uygulayan classların uygulayacağı interface'e ait metotlar da public olmak zorundadır. Aynı zamanda interfacei uygulayan metotların dönüş tipi, parametreleri birebir aynı olmak zorundadır.

interfaceler tanımlanırken, başına "I"  harfi konulması kodun okunulabilirliğini oldukça arttırır. Bu bir zorunluluk olmamakla birlikte, son derece yaygın bir kullanımdır. Böylece kalıtım aldığımız yapının bir interface olduğunu kolayca anlarız.

Aşağıdaki örneği inceleyelim:

interface ISayiSerileri {
        int SonrakiSayi();
        void Sifirla();
    }

    class CiftSayilar : ISayiSerileri {
                
        int val = -2;

        public int SonrakiSayi() {
            val += 2;
            return val;
        }

        public void Sifirla() {
            val = -2;
            Console.WriteLine("Sayaç sıfırlandı...");
        }

        public void CiftSayilariGetir(int adet) {
            StringBuilder sb = new StringBuilder ();
            for (int i = 0; i <= adet; i++)
            {
                sb.Append(SonrakiSayi() + ", ");
            }
            Console.WriteLine(adet + " adet çift sayı serisi" );
            Console.WriteLine(sb.ToString());
        }
    }

static void Main(string[] args)
        {
            CiftSayilar cs = new CiftSayilar();
            cs.CiftSayilariGetir(5);
            cs.Sifirla();
            cs.CiftSayilariGetir(7);
            Console.ReadLine();
        }

Bu programın çıktısı aşağıdaki gibi olacaktır:

5 adet çift sayı serisi
0, 2, 4, 6, 8, 10,
Sayaç sıfırlandı...
7 adet çift sayı serisi
0, 2, 4, 6, 8, 10, 12, 14,

Bu programı biraz daha inceleyelim: 

ISayiSerileri diye bir interface tanımladık. Bu interface iki tane metotda sahip:

int SonrakiSayi();
void Sifirla();

ISayiSerileri'deki tanımlara dikkat ediniz.. Herhangi bir erişim belirteci yok. Çünkü kapalı olarak publictir. Fakat bunu uygulayan CiftSayilar classındaki bu metotların tanımı publictir. public olmak zorundadır. CiftSayilar classında bu iki metottan birini çıkardığınızda derleme hatası alacaksınız. Uygulayıcı sınıflar interfacelerin bütün metotlarını uygulamak zorunda olmakla birlikte, ayrıca kendi üyelerine ve kendi metotlarına (CiftSayilariGetir(int adet)) sahip olabilirler.


Not: Daha önce kalıtım derslerinde yazdığım gibi, bir class yalnızca bir tane classı kalıtım yoluyla devralabiliyorken bir class birçok interfacei devralabilir. Bir tane class olacak şekilde, hem class hem de birden çok interfacei aynı anda uygulayabilir.

class A{....}
interface I {....}
interface IB {....}

class B : A, IA, IB {.....} 

Yukardaki B classı derlenecektir. Aynı anda hem bir tane class hem de birden çok interfacei uygulamak mümkündür.

Bununla birlikte interfaceler arasında da bir kalıtım hiyerarşisi oluşturmak da mümkündür.

Aşağıdaki örneği inceleyelim:

interface IA {
        void MethodA();
    }

    interface IB : IA {
        //Burada void MethodA()'yı tanımlamak gerekmez.
        void MethodB();
    }

    class A : IB {
        public void MethodA() { } //IA'dan gelen
        public void MethodB() { } //IB'den gelen
    }

Yukardaki örnekte, A sınıfı IB interfaceinden türemiştir, IB de IA interfaceinden türemişti. Dolayısıyla A sınıfı IA'dan gelen MethodA() ve IB'den gelen MethodB()'yi uygulamak zorundadır.

interfaceler sadece metot tanımlamaz. Aynı zamanda bir properties de tanımlayabilir. Fakat tahmin edileceği üzere get ve set belirteçleri belli bir değer gösteremez.

interfacede bir properties aşağıdaki gibi tanımlanmalıdır:

interface IA {
        int MyProperties { get; set; }
    }



Açık Uygulamalar

Bazen bir interface i uygularken bazı üyelerin private kalmasını isteyebiliriz. Bu durumda interface tanımındaki üye adıinterface adı ile birlikte tanımlayabiliriz. Üyenin bu şekilde açık olarak uygulanmasına neden olur (explicit interface member implementation). Bu tanıma bir örnek aşağıdaki gibidir:

interface IA {
        int MethodA(int i);
    }

 class A : IA {
        int IA.MethodA(int i) { //Bu kısmın private olabileceğine dikkat edin. public de olabilir
            return i / 2;
        }
    }

Daha ilginç bir örnek aşağıdaki gibidir.

  interface IEven {
        bool IsOdd(int x); //Tek mi?
        bool IsEven(int x); //Çift mi?
    }

    class MyClass : IEven {

        bool IEven.IsOdd(int x) { //private olduğuna ve tanım şekline dikkat ediniz (interface adı ile birlikte)
            if ((x % 2) != 0) return true;
            return false;
        }

        public bool IsEven(int x) { //Burası normal tanımlama
            IEven e = this;
            return !e.IsOdd(x);
        }
    }

static void Main(string[] args)
        {
            MyClass ob = new MyClass();

            bool res;
            res = ob.IsEven(4);
            if (res) Console.WriteLine("4 çift sayıdır");

            //res = ob.IsOdd(3); //Hata!! IsOdd() private. erişilemez!

            //Yalnız aşağıdaki kod doğrudur ve derlenecektir. IsOdd() metoduna IEven türünden bir referans ile erişilebilir.
            IEven ieven = (IEven)ob;
            res = ieven.IsOdd(3);
            if (res) Console.WriteLine("3 tek sayıdır");

        }

Bu programın çıktısı tahmin edileceği gibi aşağıdaki gibidir:

4 çift sayıdır
3 tek sayıdır

Yukarda gerekli açıklamaları yaptığım için ayrıca açıklama yapmaya gerek görmüyorum şimdilik.

interfacelerin referans olarak kullanımı, bağımlılıkların kaldırılması kapsamında yaygın bir kullanımı vardır. (IoC, Dependency Injection.. gibi) Fakat bunu daha sonra ele alacağız.

interface konusunu şimdilik burda noktalıyoruz.

Bir sonraki makalede buluşuncaya dek hoşçakalın, sağlıcaklakalın...




Paylaş:

0 yorum:

Yorum Gönder

Bu Blogda Ara