Menu
Coddy logo textTech

Arayüz vs Soyut Sınıf

Coddy'nin C# Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası — ders 26 / 70.

Hem arayüzler hem de soyut sınıflar, diğer sınıfların uyması gereken sözleşmeler tanımlamanıza olanak tanır, ancak farklı amaçlara hizmet ederler ve farklı yeteneklere sahiptirler.

Bir arayüz (interface), herhangi bir uygulama içermeden bir sınıfın ne yapması gerektiğini tanımlar. Bu saf bir sözleşmedir:

public interface IFlyable
{
    void Fly();
}

public class Bird : IFlyable
{
    public void Fly()
    {
        Console.WriteLine("Flapping wings");
    }
}

Bir soyut sınıf (abstract class) hem bir sözleşme hem de paylaşılan bir uygulama sağlayabilir. Bu, kısmi bir taslaktır:

public abstract class Vehicle
{
    public string Brand { get; set; }
    
    public void StartEngine()
    {
        Console.WriteLine("Engine started");
    }
    
    public abstract void Drive();  // Uygulanmalıdır
}

public class Car : Vehicle
{
    public override void Drive()
    {
        Console.WriteLine("Driving on roads");
    }
}

Temel farklar kalıtım kurallarına ve amaca dayanmaktadır.

Bir sınıf birden fazla arayüzü uygulayabilir ancak yalnızca bir soyut sınıftan miras alabilir.

İlişkisiz sınıfların bir davranışı paylaşması gerektiğinde bir arayüz (interface) kullanın (kuşlar ve uçaklar için IFlyable gibi). İlişkili sınıflar ortak kod ve durumu paylaştığında bir soyut sınıf (abstract class) kullanın (motor çalıştırma mekanizmasını paylaşan taşıtlar gibi).

Soyut sınıflar; yapıcılara, alanlara ve üyeler üzerinde erişim belirleyicilerine sahip olabilir. Geleneksel olarak arayüzler buna sahip olamazdı, ancak modern C# varsayılan uygulamalara izin verir.

Şüpheye düştüğünüzde, esneklik için arayüzleri (interfaces) tercih edin; paylaşılan bir uygulama (shared implementation) gerekli hale gelirse daha sonra her zaman bir soyut sınıf (abstract class) ekleyebilirsiniz.

challenge icon

Görev

Kolay

Arayüzlerin (interfaces) soyut sınıflara (abstract classes) karşı ne zaman kullanılacağını gösteren bir medya oynatıcı sistemi oluşturalım. Birbirinden bağımsız cihazların bir arayüz aracılığıyla oynatma davranışını paylaştığı, ilişkili medya türlerinin ise bir soyut sınıf aracılığıyla ortak uygulamayı paylaştığı bir tasarım oluşturacaksınız.

Kodunuzu dört dosya halinde düzenleyeceksiniz:

  • IPlayable.cs: Media ad alanı (namespace) içinde IPlayable adında bir arayüz tanımlayın. Bu arayüz, ister bir telefon, ister bir TV veya özel bir müzik çalar olsun, medya oynatabilen her şey için sözleşmeyi temsil eder. Geriye bir dize (string) döndüren tek bir Play() metodu bildirmelidir.
  • MediaFile.cs: Media ad alanı içinde MediaFile adında soyut bir sınıf tanımlayın. Bu, tüm medya dosyaları için paylaşılan temeli temsil eder. Bir Title özelliğine (string) ve bir Duration özelliğine (int, saniyeyi temsil eder) sahip olmalıdır. Her iki değeri de ayarlayan bir yapıcı (constructor) ekleyin. "{Title} ({Duration}s)" döndüren GetInfo() adında somut bir metot ekleyin. Ayrıca, bir dize döndüren soyut bir GetType() metodu bildirin - her medya türü ne tür bir dosya olduğunu kendisi tanımlayacaktır.
  • AudioFile.cs: Media ad alanı içinde MediaFile sınıfından miras alan ve IPlayable arayüzünü uygulayan bir AudioFile sınıfı tanımlayın. Bir Artist özelliği (string) ekleyin. Yapıcı; başlık, süre ve sanatçı bilgilerini kabul etmelidir. GetType() metodunu "Audio" döndürecek şekilde geçersiz kılın (override). Play() metodunu "Playing audio: {Title} by {Artist}" döndürecek şekilde uygulayın.
  • Program.cs: Ana dosyanızda, girdi değerlerini kullanarak bir AudioFile oluşturun. Sınıfın her iki yönünü de gösterin: GetInfo() ve GetType() metodlarını (soyut sınıftan miras alınan) çağırın, ardından Play() metodunu (arayüzden gelen) çağırın. Ses dosyasını bir IPlayable değişkeninde saklayın ve arayüz polimorfizmini göstermek için Play() metodunu tekrar çağırın.

Üç girdi alacaksınız:

  • Şarkı adı
  • Saniye cinsinden süre
  • Sanatçı adı

Çıktıyı şu formatta yazdırın:

Info: {GetInfo() result}
Type: {GetType() result}
Direct: {Play() result}
Via Interface: {Play() result from IPlayable variable}

Örneğin, girdiler Bohemian Rhapsody, 354 ve Queen ise çıktı şöyle olmalıdır:

Info: Bohemian Rhapsody (354s)
Type: Audio
Direct: Playing audio: Bohemian Rhapsody by Queen
Via Interface: Playing audio: Bohemian Rhapsody by Queen

AudioFile sınıfının her iki yaklaşımdan da nasıl yararlandığına dikkat edin: Soyut sınıftan paylaşılan uygulamayı (GetInfo()) miras alırken aynı zamanda IPlayable sözleşmesini de yerine getirir. Soyut sınıf, ilgili medya türleri için kodun yeniden kullanılmasını sağlarken, arayüz herhangi bir ilgisiz sınıfın bir kalıtım hiyerarşisini paylaşmadan "oynatılabilir" (playable) olmasına olanak tanır.

Kopya kağıdı

Bir arayüz (interface), sınıfların uyması gereken bir sözleşmeyi herhangi bir uygulama (implementation) içermeden tanımlar:

public interface IFlyable
{
    void Fly();
}

public class Bird : IFlyable
{
    public void Fly()
    {
        Console.WriteLine("Flapping wings");
    }
}

Bir soyut sınıf (abstract class), hem bir sözleşme hem de paylaşılan bir uygulama sağlar:

public abstract class Vehicle
{
    public string Brand { get; set; }
    
    public void StartEngine()
    {
        Console.WriteLine("Engine started");
    }
    
    public abstract void Drive();  // Must be implemented
}

public class Car : Vehicle
{
    public override void Drive()
    {
        Console.WriteLine("Driving on roads");
    }
}

Temel farklar:

  • Bir sınıf birden fazla arayüzü uygulayabilir ancak yalnızca bir soyut sınıftan miras alabilir
  • İlişkisiz sınıfların bir davranışı paylaşması gerektiğinde arayüzleri kullanın
  • İlişkili sınıflar ortak kod ve durumu paylaştığında soyut sınıfları kullanın
  • Soyut sınıflar yapıcılara (constructors), alanlara (fields) ve erişim belirleyicilere (access modifiers) sahip olabilir
  • Esneklik için arayüzleri tercih edin

Kendin dene

using System;
using Media;

class Program
{
    public static void Main(string[] args)
    {
        // Giriş değerlerini oku
        string title = Console.ReadLine();
        int duration = Convert.ToInt32(Console.ReadLine());
        string artist = Console.ReadLine();
        
        // TODO: Giriş değerlerini kullanarak bir AudioFile oluşturun
        
        // TODO: Info: yazdırın ve ardından GetInfo() sonucunu ekleyin
        
        // TODO: Type: yazdırın ve ardından GetType() sonucunu ekleyin
        
        // TODO: Direct: yazdırın ve ardından Play() sonucunu ekleyin
        
        // TODO: Ses dosyasını bir IPlayable değişkeninde saklayın
        
        // TODO: Via Interface: yazdırın ve ardından IPlayable değişkeninden gelen Play() sonucunu ekleyin
    }
}
quiz iconKendini test et

Bu ders kısa bir quiz içerir. Soruları yanıtlamak ve ilerlemeni kaydetmek için derse başla.

Nesne Yönelimli Programlama bölümündeki tüm dersler