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.
type Motor interface {
Calistir(surucuAdı string)
Durdur(surucuAdı string)
}
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:
package main
import "fmt"
type Motor interface {
Calistir(surucuAdı string)
Durdur(surucuAdı string)
}
// Araba ve Otobus tipleri Motor arayüzünü uygulamaktadır.
type Araba struct {
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")
}
type Otobus struct {
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")
}
func main() {
araba := Araba{Plaka: "34ABC123"} // Araba tipinde bir nesne oluşturduk
otobus := Otobus{Plaka: "34DEF456"} // Otobus tipinde bir nesne oluşturduk
var 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.
package main
import "fmt"
type Motor interface {
Calistir(surucuAdı string)
Durdur(surucuAdı string)
}
type Klima interface {
KlimaAc()
KlimaKapat()
}
// Araba tipi Motor ve Klima arayüzlerini uyguladığı için Araba tipi Motor ve Klima arayüzlerine atayabiliyoruz
type Araba struct {
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ı")
}
func main() {
araba := Araba{Plaka: "34ABC123"} // Araba tipinde bir nesne oluşturduk
var motor Motor // Motor arayüzü tipinde bir değişken tanımladık
var 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.
package main
import "fmt"
type Motor interface {
Calistir(surucuAdı string)
Durdur(surucuAdı string)
}
type Araba struct {
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 main() {
araba := Araba{Plaka: "34ABC123"} // Araba tipinde bir nesne oluşturduk
var 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ım
switch t := motor.(type) {
case Araba:
fmt.Println(t.Plaka, "bir arabadır")
case Otobus:
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.