Golang, basit ve güçlü bir programlama dili olmasının yanı sıra arayüzler aracılığıyla esnek ve modüler kod yazma imkanı da sunar. Bu yazıda, arayüz (interface) temellerini, nasıl oluşturulduğunu ve kullanıldığını detaylı bir şekilde inceleyeceğiz.
Arayüz Nedir?
Arayüz, bir türün sahip olması gereken yöntemlerin bir listesini tanımlar. Farklı türler bu arayüzü uygulayarak, arayüzde tanımlanan tüm yöntemleri kendi veri yapılarına ve işlevlerine göre özelleştirebilir. Bu sayede farklı türler arasında tutarlı bir API oluşturmak ve kodun modülerliğini ve tekrar kullanılabilirliğini artırmak mümkün olur.
Arayüz Oluşturma
interface anahtar sözcüğü kullanılarak yeni bir arayüz tanımlayabilirsiniz. Arayüz, bir veya birden fazla metodu içerir. Metod, metod adını, parametrelerini ve dönüş değerini belirtir.
Burada Motor adında bir arayüz oluşturduk ve bu arayüzümüzün surucuAdı parametresini alan iki metodu bulunmakta. Şimdi bu arayüzümüzü tır ve araba adındaki iki türlerimize uygulayalım:
packagemainimport"fmt"typeMotorinterface {Calistir(surucuAdı string)Durdur(surucuAdı string)}// Araba ve Otobus tipleri Motor arayüzünü uygulamaktadır.typeArabastruct { Plaka string}func (a Araba) Calistir(surucuAdı string) { fmt.Println(a.Plaka, "plakalı araba", surucuAdı, "tarafından çalıştırıldı")}func (a Araba) Durdur(surucuAdı string) { fmt.Println(a.Plaka, "plakalı araba", surucuAdı, "tarafından durduruldu")}typeOtobusstruct { Plaka string}func (o Otobus) Calistir(surucuAdı string) { fmt.Println(o.Plaka, "plakalı otobüs", surucuAdı, "tarafından çalıştırıldı")}func (o Otobus) Durdur(surucuAdı string) { fmt.Println(o.Plaka, "plakalı otobüs", surucuAdı, "tarafından durduruldu")}funcmain() { araba :=Araba{Plaka: "34ABC123"} // Araba tipinde bir nesne oluşturduk otobus :=Otobus{Plaka: "34DEF456"} // Otobus tipinde bir nesne oluşturdukvar motor Motor// Motor arayüzü tipinde bir değişken tanımladık motor = araba // motor değişkenine araba nesnesini atadık motor.Calistir("Ahmet") // motor değişkeni üzerinden Calistir metodunu çağırdık motor.Durdur("Ahmet") // motor değişkeni üzerinden Durdur metodunu çağırdık motor = otobus // motor değişkenine otobus nesnesini atadık motor.Calistir("Mehmet") // motor değişkeni üzerinden Calistir metodunu çağırdık motor.Durdur("Mehmet") // motor değişkeni üzerinden Durdur metodunu çağırdık}
Output
34ABC123 plakalı araba Ahmet tarafından çalıştırıldı
34ABC123 plakalı araba Ahmet tarafından durduruldu
34DEF456 plakalı otobüs Mehmet tarafından çalıştırıldı
34DEF456 plakalı otobüs Mehmet tarafından durduruldu
Arayüzlerle Neler Yapılabilir?
Arayüzler aşağıdakiler için kullanılabilir:
Farklı türler arasında tutarlı bir şekilde etkileşime girmek: Bir türün arayüzü uyguladığını biliyorsanız o türle belirli bir şekilde etkileşime girebilirsiniz. Bu, kodunuzu daha soyut ve yeniden kullanılabilir hale getirir.
Polimorfizmi uygulamak: Farklı türlerin aynı arayüzü uyguladığı durumlarda bu türleri tek bir koleksiyonda saklayabilir ve onlarla tek bir şekilde etkileşime girebilirsiniz.
Arayüzlerle İlgili Önemli Noktalar:
Bir tür birden fazla arayüzü uygulayabilir.
packagemainimport"fmt"typeMotorinterface {Calistir(surucuAdı string)Durdur(surucuAdı string)}typeKlimainterface {KlimaAc()KlimaKapat()}// Araba tipi Motor ve Klima arayüzlerini uyguladığı için Araba tipi Motor ve Klima arayüzlerine atayabiliyoruztypeArabastruct { Plaka string}func (a Araba) Calistir(surucuAdı string) { fmt.Println(a.Plaka, "plakalı araba", surucuAdı, "tarafından çalıştırıldı")}func (a Araba) Durdur(surucuAdı string) { fmt.Println(a.Plaka, "plakalı araba", surucuAdı, "tarafından durduruldu")}func (a Araba) KlimaAç() { fmt.Println(a.Plaka, "plakalı araba kliması açıldı")}func (a Araba) KlimaKapat() { fmt.Println(a.Plaka, "plakalı araba kliması kapatıldı")}funcmain() { araba :=Araba{Plaka: "34ABC123"} // Araba tipinde bir nesne oluşturdukvar motor Motor// Motor arayüzü tipinde bir değişken tanımladıkvar klima Klima// Klima arayüzü tipinde bir değişken tanımladık klima = araba // Araba tipi Klima arayüzünü uyguladığı için klima değişkenine atayabildik motor = araba // Araba tipi Motor arayüzünü uyguladığı için motor değişkenine atayabildik motor.Calistir("Ahmet") // 34ABC123 plakalı araba Ahmet tarafından çalıştırıldı motor.Durdur("Mehmet") // 34ABC123 plakalı araba Mehmet tarafından durduruldu klima.KlimaAç() // 34ABC123 plakalı araba kliması açıldı klima.KlimaKapat() // 34ABC123 plakalı araba kliması kapatıldı}
Output:
34ABC123 plakalı araba Ahmet tarafından çalıştırıldı
34ABC123 plakalı araba Ahmet tarafından durduruldu
34DEF456 plakalı otobüs Mehmet tarafından çalıştırıldı
34DEF456 plakalı otobüs Mehmet tarafından durduruldu
Arayüzler değer türleri veya referans türleri olabilir.
packagemainimport"fmt"typeMotorinterface {Calistir(surucuAdı string)Durdur(surucuAdı string)}typeArabastruct { Plaka string}func (a Araba) Calistir(surucuAdı string) { fmt.Println(a.Plaka, "plakalı araba", surucuAdı, "tarafından çalıştırıldı")}func (a Araba) Durdur(surucuAdı string) { fmt.Println(a.Plaka, "plakalı araba", surucuAdı, "tarafından durduruldu")}funcmain() { araba :=Araba{Plaka: "34ABC123"} // Araba tipinde bir nesne oluşturdukvar motor1 Motor= araba // Araba tipi Motor arayüzüne atandı (Değer türü)var motor2 Motor=&araba // Araba tipi Motor arayüzüne atandı (Referans türü) motor1.Calistir("Ahmet") // motor1 değişkeni üzerinden Calistir fonksiyonu çağrıldı motor1.Calistir("Mehmet") // motor2 değişkeni üzerinden Calistir fonksiyonu çağrıldı araba.Plaka ="DEĞİŞTİR"// Araba nesnesinin plakasını değiştirdik motor1.Durdur("Ahmet") // motor1 değişkeni üzerinden Durdur fonksiyonu çağrıldı motor2.Durdur("Mehmet") // motor2 değişkeni üzerinden Durdur fonksiyonu çağrıldı}
Output
34ABC123 plakalı araba Ahmet tarafından çalıştırıldı
34ABC123 plakalı araba Mehmet tarafından çalıştırıldı
34ABC123 plakalı araba Ahmet tarafından durduruldu
DEĞİŞTİR plakalı araba Mehmet tarafından durduruldu
Görüldüğü üzere motor1 arayüzü araba türümüzü değer türü olarak aldığı için bellekte kopyaladı. Bu nedenle asıl araba türümüzdeki değişiklik bu arayüze yansımadı. Fakat motor2 arayüzü araba türümüzü referans türü olarak aldığı için, yani araba türümüze bellekteki adresinden ulaştığı için, araba türümüzdeki değişiklikler yansıdı.
Arayüz ile Türlerin Güvenli Dönüşümleri
Go dilinde bir arayüzden bir diğeri türüne güvenli bir şekilde dönüşüm yapmak için tip doğrulama veya tip iddiası (type assertion) kullanılabilir. Bu sayede bir arayüz tipinden başka bir tipe dönüşüm yaparken çalışma zamanı hatalarının önüne geçilmiş olur.
Tip Doğrulaması (Type Assertion)
Tip iddiası bir değişkenin belirli bir türe sahip olduğunu doğrulamak için kullanılır. Eğer değişken iddia edilen tipe sahipse dönüşüm başarılı bir şekilde gerçekleşir; aksi takdirde program bir panic hatası üretir.
var motor Motor=Araba{Plaka: "34ABC123"}if araba, ok := motor.(Araba); ok { fmt.Println(araba.Plaka, "bir arabadır.")} else { fmt.Println("Motor bir arabaya dönüştürülemedi.")}
Tip Değişkeni ile Güvenli Dönüşüm
Go, tip değişkeni (type switch) kullanarak bir değişkenin birden fazla türü kontrol etmeye ve her türe göre farklı işlemler gerçekleştirmeye olanak tanır. Bu, birden fazla tip iddiasını birleştirir ve kodun daha temiz olmasını sağlar.
var motor Motor= getMotor() // getMotor fonksiyonunun herhangi bir Motor türünde nesne döndürdüğünü varsayalımswitch t := motor.(type) {caseAraba: fmt.Println(t.Plaka, "bir arabadır")caseOtobus: fmt.Println(t.Plaka, "bir otobüstür")default: fmt.Println("Bilinmeyen tür")}
Sonuç
Arayüzlerin kullanımı, Go'nun tip sisteminin güçlü ve esnek özelliklerinden biridir. Farklı türlerdeki nesneleri tek bir arayüz altında toplayarak kodun genel anlamda daha temiz ve yönetilebilir olmasını sağlar. Tip doğrulaması ve tip değişkenleri ile tür dönüşümleri güvenli ve kontrol altında gerçekleştirilebilir; bu da çalışma zamanı hatalarını minimize eder.