Herkese merhaba. Bir önceki yazımda abstraction konusunu detaylı bir şekilde anlatmıştım. Bu yazıda, abstract class ve interface arasındaki farkları inceleyeceğiz. İki yöntem de kullanım açısından açısından benzer olsa da aralarında bazı farklılıklar bulunmaktadır. İki yöntemi de ayrı ayrı başlıklar altında inceleyelim. Örneklere github hesabımdan ulaşabilirsiniz.

Abstract vs Interface

Abstract Class Özellikleri

Genel özelliklerini hatırlarsak;

  • Constructor ve destructor metotları barındırabilirler.
  • İçerisinde değişken ve gövdeli metot barındırabilir.
  • new keywordü ile instance’ı oluşturulamaz.
  • Çoğunlukla kalıtım yapılırken tercih edilirler.
  • Çoklu kalıtım C#’ta desteklenmemektedir, bu yüzden bir sınıfa yalnızca bir abstract class kalıtım yoluyla aktarılır.

Genellikle abstract classlar ile kalıtım alacak class arasında “is-a” ilişkisi vardır. Örnek vermek gerekirse; “Ferrari is a car” cümlesini inceleyebiliriz. Sonuçta Ferrari de bir arabadır ve araba sınıfında bulunan bütün ortak özellikleri içerisinde barındırır.

abstract class Car
{
    public string Brand { get; set; }
    public string Color { get; set; }

    public abstract string MaximumSpeed();
}

internal class Ferrari : Car
{
    public override string MaximumSpeed()
    {
        return "Ferrari'nin maximum hızı: 300";
    }
}

Ortak özellikleri Car sınıfında topladığımız için bir başka araba eklemek istediğimizde Car sınıfından türetebiliriz. Abstract class içerisinde bulunan abstract metotların kalıtım verilen classlarda ezilmesi gerektiğinden, Ferrari içerisinde MaximumSpeed metodu ezilmiştir.

Interface Özellikleri

C# içerisindeki 5 temel tipten birisidir ve referans tiplidir. Interfaceler içerisinde metotların ve özelliklerin sadece imzalarını barındırır; gövdeli metot ve normal üyeler barındırmaz. Interface’leri tanımlarken interface keywordü kullanırız. Best-practice olarak genellikle interface olarak oluşturacağımız dosyanın başına I harfini ekleriz.

Interfacelerin default erişim belirleyicisi internaldir. İçerisinde bulunan üyelerin erişim belirleyicisi publictir ve değiştirilemez. İçerisindeki üyelere erişim belirleyicisi yazdığımızda IDE hata verir. Örnek olarak matematik kütüphanesi oluşturalım.

Interface Erişim Belirleyici Uyarı

Toplama metodunun erişim belirleyicisini değiştirmek istediğimizde Visual Studio otomatik olarak altını çizmektedir.

Interfaceler tamamen soyut yapılardır bu yüzden somut üyeler barındırmazlar. Tamamen soyut olduğu için new keywordü ile nesnesi oluşturulamaz. Sadece metot imzası, property ve indexer elemanlarını barındırırlar. Classlara üyelerini kalıtım yoluyla değil implement edilerek aktarır. Bir class birden fazla interface’i implement edebilir, fakat bir interface sadece başka bir interface’e kalıtım yoluyla aktarılır.

Interfacelerin amacı yetenek kazandırmaktır. Bu nedenle, interface ile implement edilecek class arasında “can-do” ilişkisi vardır.

internal interface IMath
{
    int Addition(int num1, int num2);
    int Subtraction(int num1, int num2);
    int Multiplication(int num1, int num2);
    int Division(int num1, int num2);
    string Name { get; }
}

internal class MathLib : IMath
{
    public string Name => "Matematik Kütüphanesi";

    public int Addition(int num1, int num2)
    {
        return num1 + num2;
    }

    public int Division(int num1, int num2)
    {
        return (num1 / num2);
    }

    public int Multiplication(int num1, int num2)
    {
        return num1 * num2;
    }

    public int Subtraction(int num1, int num2)
    {
        return num1 - num2;
    }
}

static void Main(string[] args)
{
    MathLib mathLib = new MathLib();
    Console.WriteLine(mathLib.Name);
    int num1, num2;
    Console.WriteLine("Lütfen ilk sayıyı giriniz: ");
    num1 = int.Parse(Console.ReadLine());
    Console.WriteLine("Lütfen ikinci sayıyı giriniz: ");
    num2 = int.Parse(Console.ReadLine());
    Console.WriteLine($" Toplam : {mathLib.Addition(num1, num2)} {Environment.NewLine} Çarpım : {mathLib.Multiplication(num1, num2)} \n Fark : {mathLib.Subtraction(num1, num2)} \n Bölüm: {mathLib.Division(num1, num2)}");
    Console.ReadKey();
}

Interface Örnek

Interface implement edildiği classa yetenek kazandırır demiştik. Bunu daha iyi açıklamak için bir örnek tasarlayalım. Balıklar ve denizaltılar yüzme yeteneğine sahipken, kuşlar ve uçaklar da uçma yeteneğine sahiptir. Ördekler ise bu 2 yeteneğe birden sahipler. Ördek sınıfı 2 sınıftan kalıtım alamayacağı için yüzme ve uçma yeteneklerini interface olarak tasarlamamız gerekir.

internal interface ISwim
{
    string Swim();
    string Dive();
    string GetOut();
}
internal interface IFly
{
    string Fly();
    string Settle();
}

static void Main(string[] args)
{
    Bird bird = new Bird();
    Plane plane = new Plane();
    Fish fish = new Fish();
    Submarine submarine = new Submarine();
    Duck duck = new Duck();

    Console.WriteLine("-----Kuş-----");
    Console.WriteLine(bird.Fly() + Environment.NewLine + bird.Settle());
    Console.WriteLine("-----Uçak-----");
    Console.WriteLine(plane.Fly() + Environment.NewLine + plane.Settle());
    Console.WriteLine();
    Console.WriteLine("-----Balık-----");
    Console.WriteLine(fish.Swim() + Environment.NewLine + fish.Dive() + Environment.NewLine + fish.GetOut());
    Console.WriteLine("-----Denizaltı-----");
    Console.WriteLine(submarine.Swim() + Environment.NewLine + submarine.Dive() + Environment.NewLine + submarine.GetOut());
    Console.WriteLine();
    Console.WriteLine("-----Ördek-----");
    Console.WriteLine(duck.Fly() + Environment.NewLine + duck.Settle());
    Console.WriteLine(duck.Swim() + Environment.NewLine + duck.Dive() + Environment.NewLine + duck.GetOut());
    Console.ReadKey();
}

Aralarında ufak farklar olsa da işlevsel olarak oldukça benzer yapılardır. Abstract class ve interface konusuyla ile ilgili bildiklerimi bir araya getirmeye çalıştım. Bir sonraki yazıya kadar kendinize çok iyi bakın! 😊