1 Arayüzlerin Tanıtımı
1.1 Arayüz Nedir
Go dilinde bir arayüz, bir türdür, soyut bir türdür. Arayüz, belirli bir uygulamanın ayrıntılarını gizler ve yalnızca nesnenin davranışını kullanıcıya gösterir. Arayüz, bir dizi yöntemi tanımlar, ancak bu yöntemler herhangi bir işlevsellik gerçekleştirmez; bunun yerine, bunlar belirli bir tür tarafından sağlanır. Go dilinin arayüzlerin özelliği, bir türün hangi arayüzü uyguladığını açıkça bildirmesi gerekmemesidir; yalnızca arayüz tarafından gereken yöntemleri sağlaması yeterlidir.
// Bir arayüzü tanımlama
type Okuyucu interface {
Oku(p []byte) (n int, hata error)
}
Bu Okuyucu
arayüzünde, Oku(p []byte) (n int, hata error)
yöntemini uygulayan herhangi bir tür, Okuyucu
arayüzünü uyguladığı söylenebilir.
2 Arayüzün Tanımı
2.1 Arayüzün Sözdizimi Yapısı
Go dilinde bir arayüzün tanımı şu şekildedir:
type arayüzAdı interface {
yöntemAdı(parametreListesi) dönüşTipleriListesi
}
-
arayüzAdı
: Arayüzün adı, Go'nun isimlendirme kuralını izler ve büyük harfle başlar. -
yöntemAdı
: Arayüzün gerektirdiği yöntemin adı. -
parametreListesi
: Yöntemin parametre listesi, parametreler virgülle ayrılmıştır. -
dönüşTipleriListesi
: Yöntemin dönüş tipleri listesi.
Bir tür, arayüzdeki tüm yöntemleri uygularsa, o tür arayüzü uygular.
type İşçi interface {
Çalış()
Dinlen()
Yukarıdaki İşçi
arayüzünde, Çalış()
ve Dinlen()
yöntemlerine sahip herhangi bir tür, İşçi
arayüzünü karşılar.
3 Arayüz Uygulama Mekanizması
3.1 Arayüzleri Uygulamanın Kuralları
Go dilinde, bir türün, bir arayüzü uygulayabilmek için sadece arayüzdeki tüm yöntemleri uygulaması gerekir. Bu uygulama, bazı diğer dillerde olduğu gibi açıkça bildirilmesi gerekmeyen, zımni bir uygulamadır. Arayüzleri uygulamanın kuralları şunlardır:
- Arayüzü uygulayan tür, bir yapı veya başka özel tür olabilir.
- Bir türün, arayüzdeki tüm yöntemleri uygulamış olması, o türün o arayüzü uyguladığı anlamına gelir.
- Arayüzdeki yöntemler, arayüzdeki yöntemlerin adı, parametre listesi ve dönüş değerleri de dahil olmak üzere, kesinlikle aynı yöntem imzasına sahip olmalıdır.
- Bir tür, aynı anda birden fazla arayüzü uygulayabilir.
3.2 Örnek: Arayüzü Uygulamak
Şimdi belirli bir örnek üzerinden arayüzlerin uygulanma sürecini ve yöntemlerini gösterelim. Konuşmacı
arayüzünü düşünelim:
type Konuşmacı interface {
Konuş() string
}
İnsan
türünün Konuşmacı
arayüzünü uygulaması için, İnsan
türü için bir Konuş
yöntemi tanımlamamız gerekiyor:
type İnsan struct {
İsim string
}
// Konuş metodu, İnsan türünün Konuşmacı arayüzünü uygulamasını sağlar.
func (i İnsan) Konuş() string {
return "Merhaba, benim adım " + i.İsim
}
func main() {
var konuşmacı Konuşmacı
james := İnsan{"James"}
konuşmacı = james
fmt.Println(konuşmacı.Konuş()) // Çıktı: Merhaba, benim adım James
}
Yukarıdaki kodda, İnsan
yapısı, Konuş
yöntemini uygulayarak Konuşmacı
arayüzünü uygular. main
işlevinde, İnsan
türü değişkeni james
, Konuşmacı
türünde değişkeni konuşmacı
'ya atanır çünkü james
, Konuşmacı
arayüzünü karşılar.
4 Arayüz Kullanmanın Faydaları ve Kullanım Alanları
4.1 Arayüzlerin Kullanımının Faydaları
Arayüzlerin kullanımının birçok faydası vardır:
- Bağlantısızlık: Arayüzler, kodumuzun belirli uygulama ayrıntılarından ayrılmasını sağlayarak kod esnekliğini ve sürdürülebilirliği artırır.
- Değiştirilebilirlik: Arayüzler, yeni uygulama, varsa, eski uygulamayı kolayca değiştirmemizi sağlar, yeter ki yeni uygulama aynı arayüzü karşılarsa.
- Genişletilebilirlik: Arayüzler, mevcut kodu değiştirmeden bir programın işlevselliğini genişletmemize izin verir.
- Test Etme Kolaylığı: Arayüzler, birim test etmeyi basit hale getirir. Arayüzleri test etmek için sahte nesneleri kullanabiliriz.
- Polimorfizma: Arayüzler, farklı nesnelerin farklı senaryolarda aynı iletiye farklı şekillerde yanıt vermesini gerçekleştirir.
4.2 Arayüzlerin Uygulama Senaryoları
Arayüzler, Go dilinde geniş bir şekilde kullanılmaktadır. İşte bazı tipik uygulama senaryoları:
-
Standart Kütüphanedeki Arayüzler: Örneğin,
io.Reader
veio.Writer
arayüzleri dosya işleme ve ağ programlaması için yaygın bir şekilde kullanılır. -
Sıralama:
Len()
,Less(i, j int) bool
veSwap(i, j int)
metodlarınısort.Interface
arayüzünde uygulayarak herhangi bir özel dilimi sıralamak mümkün olur. -
HTTP İşleyicileri:
http.Handler
arayüzündeServeHTTP(ResponseWriter, *Request)
metodunu uygulayarak özel HTTP işleyicileri oluşturulabilir.
İşte sıralama için arayüzlerin kullanımına dair bir örnek:
package main
import (
"fmt"
"sort"
)
type YasDizisi []int
func (a YasDizisi) Len() int { return len(a) }
func (a YasDizisi) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a YasDizisi) Less(i, j int) bool { return a[i] < a[j] }
func main() {
yaslar := YasDizisi{45, 26, 74, 23, 46, 12, 39}
sort.Sort(yaslar)
fmt.Println(yaslar) // Çıktı: [12 23 26 39 45 46 74]
}
Bu örnekte, sort.Interface
'in üç metodunu uygulayarak YasDizisi
dilimini sıralayabiliyoruz ve bu da arayüzlerin var olan tiplerin davranışını genişletme yeteneğini gösteriyor.
5. Arayüzlerin İleri Düzey Özellikleri
5.1 Boş Arayüz ve Uygulama Alanları
Go dilinde, boş arayüz herhangi bir metod içermeyen özel bir arayüz tipidir. Bu nedenle neredeyse her türlü değer boş bir arayüz olarak kabul edilebilir. Boş arayüz, interface{}
kullanılarak temsil edilir ve Go'da son derece esnek bir tip olarak birçok önemli rol oynar.
// Boş bir arayüz tanımlama
var herhangi interface{}
Dinamik Tür İşleme:
Boş arayüz herhangi bir türden değeri depolayabilir, bu da belirsiz tiplerle başa çıkmak için çok kullanışlıdır. Örneğin, farklı türlerde parametreleri kabul eden bir işlev oluşturduğunuzda, boş arayüz parametre türü olarak kullanılabilir ve herhangi bir türde veri kabul edebilir.
func HerhangiSeyiYazdır(v interface{}) {
fmt.Println(v)
}
func main() {
HerhangiSeyiYazdır(123)
HerhangiSeyiYazdır("merhaba")
HerhangiSeyiYazdır(struct{ isim string }{isim: "Gopher"})
}
Yukarıdaki örnekte, HerhangiSeyiYazdır
işlevi boş arayüz türünden v
parametresini alır ve yazdırır. HerhangiSeyiYazdır
, bir tamsayı, string veya yapı verildiğinde başa çıkabilir.
5.2 Arayüz Gömme (Embedding)
Arayüz gömme, bir arayüzün başka bir arayüzün tüm metodlarını içermesi ve belki de bazı yeni metodları eklemesine işaret eder. Bu, arayüz tanımında diğer arayüzleri gömerek gerçekleştirilir.
type Okuyucu interface {
Read(p []byte) (n int, err error)
}
type Yazıcı interface {
Write(p []byte) (n int, err error)
}
// ReadWriter arayüzü, Reader ve Writer arayüzlerini gömümler
type OkumaYazma interface {
Okuyucu
Yazıcı
}
Arayüz gömme kullanarak daha modüler ve hiyerarşik bir arayüz yapısı oluşturabiliriz. Bu örnekte, OkumaYazma
arayüzü, Okuyucu
ve Yazıcı
arayüzlerinin metodlarını birleştirerek okuma ve yazma işlevselliğini birleştirir.
5.3 Arayüz Türü İddiası
Tür iddiası, arayüz tür değerlerini kontrol etme ve dönüştürme işlemidir. Bir arayüz türünden belirli bir tür değerini çıkarmamız gerektiğinde tür iddiası çok kullanışlı hale gelir.
İddia temel sözdizimi:
değer, başarılıMı := arayüzDeğeri.(Tür)
Eğer iddia başarılıysa, değer
altta yatan Tür
'ün değeri olacaktır ve başarılıMı
doğru olacaktır; iddia başarısız olursa, değer
Tür
'ün sıfır değeri olacak ve başarılıMı
yanlış olacaktır.
var i interface{} = "merhaba"
// Tür iddiası
s, basariliMi := i.(string)
if basariliMi {
fmt.Println(s) // Çıktı: merhaba
}
// Gerçek olmayan türün iddiası
f, basariliMi := i.(float64)
if !basariliMi {
fmt.Println("İddia başarısız!") // Çıktı: İddia başarısız!
}
Uygulama senaryoları:
Tür iddiası, boş bir arayüzdeki interface{}
değerlerinin türünü belirlemek ve dönüştürmek için yaygın bir şekilde kullanılır veya birden fazla arayüzü uygulama durumunda belirli bir arayüzü uygulayan türün çıkartılmasında kullanılır.
5.4 Arayüz ve Polimorfizm
Polimorfizm, nesne yönelimli programlamada bir temel kavram olup, farklı veri tiplerinin belirli tiplerle ilgilenmeksizin, yalnızca ara yüzler aracılığıyla birleştirilmiş bir şekilde işlenmesine olanak tanır. Go dilinde, polimorfizmi başarmak için ara yüzlere dayalı olarak işlem yapılır.
Ara yüzler aracılığıyla polimorfizm uygulamak
type Shape interface {
Area() float64
}
type Rectangle struct {
Width, Height float64
}
type Circle struct {
Radius float64
}
// Dikdörtgen, Shape ara yüzünü uygular
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
// Daire, Shape ara yüzünü uygular
func (c Circle) Area() float64 {
return math.Pi * c.Radius * c.Radius
}
// Farklı şekillerin alanını hesapla
func CalculateArea(s Shape) float64 {
return s.Area()
}
func main() {
r := Rectangle{Width: 3, Height: 4}
c := Circle{Radius: 5}
fmt.Println(CalculateArea(r)) // Çıktı: dikdörtgenin alanı
fmt.Println(CalculateArea(c)) // Çıktı: dairenin alanı
}
Bu örnekte, Shape
ara yüzü farklı şekiller için bir Area
metodunu tanımlar. Hem Rectangle
hem de Circle
somut tipleri bu ara yüzü uygular, yani bu tipler alan hesaplama yeteneğine sahiptir. CalculateArea
fonksiyonu, Shape
ara yüzü tipinde bir parametre alır ve Shape
ara yüzünü uygulayan herhangi bir şeklin alanını hesaplayabilir.
Bu sayede, CalculateArea
fonksiyonunun uygulamasını değiştirmeye gerek duymadan yeni şekil tipleri eklemek kolaydır. İşte polimorfizmin kodlara getirdiği esneklik ve genişletilebilirlik budur.