Interface Karşılama Kuralları
Coddy'nin GO Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası — ders 43 / 107.
Go, örtük interface karşılama için duck typing kullansa da, bir tipin bir interface'i gerçekten karşılayıp karşılamadığını belirleyen belirli kurallar vardır. Bu kuralları anlamak, sinsi hatalardan kaçınmanıza yardımcı olur.
En önemli kural pointer alıcıları (pointer receivers) ile ilgilidir. Eğer bir metot bir pointer alıcısı ile tanımlanmışsa, arayüzü değerin kendisi değil, yalnızca o türe ait bir pointer karşılar:
type Saver interface {
Save() string
}
type Document struct{ Name string }
func (d *Document) Save() string { // pointer alıcısı
return "Saved: " + d.Name
}
func Process(s Saver) {
fmt.Println(s.Save())
}
Save() bir pointer alıcısına (pointer receiver) sahip olduğu için, Saver arayüzünü yalnızca *Document karşılar:
func main() {
doc := Document{Name: "report.txt"}
Process(&doc) // çalışır - pointer arayüzü karşılar
// Process(doc) // derleme hatası - değer arayüzü karşılamaz
}
Ancak, bunun tersi daha esnektir. Eğer bir metodun değer alıcısı (value receiver) varsa, hem değer hem de işaretçi (pointer) türleri arayüzü (interface) karşılar. Go, değer alıcısı metodlarını çağırırken işaretçilerin başvurusunu (dereference) otomatik olarak çözer:
func (d Document) Info() string { // değer alıcısı
return d.Name
}
// Hem Document hem de *Document, Info() gerektiren bir arayüzü karşılar
Bu asimetri mevcuttur çünkü Go bir işaretçiden (pointer) her zaman bir değer alabilir (dereferencing yaparak), ancak bir değerden her zaman bir işaretçi alamaz (değer adreslenebilir olmayabilir). Bu kuralı akılda tutmak, arayüzlerle (interfaces) çalışırken kafa karıştırıcı derleyici hatalarını önler.
Görev
Kolayİşaretçi (pointer) ve değer (value) alıcılarının (receivers) arayüz (interface) uyumluluğunu nasıl etkilediğini gösteren bir yapılandırma sistemi oluşturalım. Alıcı seçiminin; bir arayüzle değerlerin mi, işaretçilerin mi yoksa her ikisinin mi kullanılabileceğini nasıl belirlediğini gösteren türler oluşturacaksınız.
Kodunuzu üç dosya halinde düzenleyeceksiniz:
config.go: İki metot gerektiren birConfigurablearayüzü tanımlayın:GetValue() stringveSetValue(string). Ardından iki yapılandırma türü oluşturun:Valuealanına sahipReadOnlyConfig—GetValue()için bir değer alıcısı (Value değerini döndürür) veSetValue()için bir işaretçi alıcısı (Value değerini günceller) kullanın.Dataalanına sahipSetting—her iki metot için de değer alıcıları kullanın (GetValue, Data değerini döndürür; SetValue hiçbir şeyi değiştirmeden sadece "Cannot modify" yazdırır).
processor.go: BirConfigurableve yeni bir değer dizesi (string) kabul edenProcessConfigadında bir fonksiyon oluşturun. Bu fonksiyon,GetValue()kullanarak mevcut değeri yazdırmalı, yeni değerleSetValue()fonksiyonunu çağırmalı ve ardından herhangi bir değişikliği göstermek için değeri tekrar yazdırmalıdır.main.go: Girdiden yapılandırma ayrıntılarını okuyun ve arayüz uyumluluk kurallarını gösterin:- Bir
ReadOnlyConfigoluşturun veProcessConfigfonksiyonuna bir işaretçi (pointer) geçirin (bu gereklidir çünküSetValuebir işaretçi alıcısına sahiptir). - Bir
Settingoluşturun ve değeri doğrudanProcessConfigfonksiyonuna geçirin (bu çalışır çünkü her iki metot da değer alıcılarına sahiptir).
- Bir
Aşağıdaki girdiler sağlanacaktır:
- Satır 1: ReadOnlyConfig için başlangıç değeri
- Satır 2: ReadOnlyConfig için ayarlanacak yeni değer
- Satır 3: Setting için başlangıç değeri
- Satır 4: Setting için denenecek yeni değer
ProcessConfig fonksiyonunuz şu formatta çıktı vermelidir:
Current: [value]
Current: [value after SetValue]Örneğin, debug, production, localhost ve remote verildiğinde çıktınız şöyle olmalıdır:
Current: debug
Current: production
Current: localhost
Cannot modify
Current: localhostReadOnlyConfig'in değerini gerçekten değiştirdiğine (çünkü bir işaretçi geçirdik), Setting'in ise değişmeden kaldığına (değer alıcılı SetValue orijinali değiştiremez) dikkat edin. Buradaki temel içgörü, ReadOnlyConfig değerlerinin tek başına Configurable arayüzünü karşılamayacağı —yalnızca işaretçilerin karşılayacağı— buna karşın Setting değerlerinin tüm metotları değer alıcısı kullandığı için doğrudan çalışacağıdır.
Kopya kağıdı
Go, örtük arayüz (interface) karşılama için duck typing kullanır, ancak işaretçi (pointer) ve değer (value) alıcıları (receivers) hakkında belirli kurallar vardır:
İşaretçi Alıcı Kuralı (Pointer Receiver Rule)
Eğer bir metodun bir işaretçi alıcısı (pointer receiver) varsa, yalnızca o türe ait bir işaretçi arayüzü karşılar:
type Saver interface {
Save() string
}
type Document struct{ Name string }
func (d *Document) Save() string { // işaretçi alıcısı
return "Saved: " + d.Name
}
func main() {
doc := Document{Name: "report.txt"}
Process(&doc) // çalışır - işaretçi arayüzü karşılar
// Process(doc) // derleme hatası - değer arayüzü karşılamaz
}Değer Alıcı Kuralı (Value Receiver Rule)
Eğer bir metodun bir değer alıcısı (value receiver) varsa, hem değer hem de işaretçi türleri arayüzü karşılar:
func (d Document) Info() string { // değer alıcısı
return d.Name
}
// Hem Document hem de *Document, Info() gerektiren bir arayüzü karşılarBu Asimetri Neden Var?
Go, bir işaretçiden her zaman bir değer alabilir (dereferencing yoluyla), ancak bir değerden her zaman bir işaretçi alamaz (değer adreslenebilir olmayabilir).
Kendin dene
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Girdileri oku
scanner.Scan()
rocInitial := scanner.Text()
scanner.Scan()
rocNew := scanner.Text()
scanner.Scan()
settingInitial := scanner.Text()
scanner.Scan()
settingNew := scanner.Text()
// TODO: rocInitial değeri ile bir ReadOnlyConfig oluşturun
// TODO: ProcessConfig'e bir POINTER (işaretçi) geçirin (SetValue'nun işaretçi alıcısı olduğu için gereklidir)
// TODO: settingInitial değeri ile bir Setting oluşturun
// TODO: DEĞERİ doğrudan ProcessConfig'e geçirin (her iki metodun da değer alıcısı olduğu için çalışır)
// Kullanılmayan değişken hatalarını önlemek için bu değişkenleri kullanın
_ = rocInitial
_ = rocNew
_ = settingInitial
_ = settingNew
}
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
1Go OOP Temelleri
Harici DosyalarGo Çalışma Alanı ve ModüllerPaketler ve İçe AktarmalarDışa Aktarılan ve Aktarılmayan İsimlerGo'da OOP'ye GirişSınıf Olarak Struct'larStruct'larda Metot TanımlamaPointer ve Değer AlıcılarStruct BaşlatmaYapıcı FonksiyonlarÖzet - Basit Hesap Makinesi4Arayüzler
Arayüzlere GirişÖrtük UygulamaSözleşme Olarak ArayüzBoş Arayüz (any)Tür OnaylamaTür SeçimiArayüz BileşimiStringer ve Error ArayüzleriÖzet - Şekil Hesaplayıcı7Kapsülleme
Dışa Aktarılan ve Aktarılmayan AlanlarPaket Seviyesinde KapsüllemeGetter ve Setter MetotlarıGo'da Bilgi GizlemeÖzet - Öğrenci Kayıtları10Generics (Go 1.18+)
Generics'e GirişTür ParametreleriTür KısıtlamalarıGeneric Struct'larGeneric Metotlar İçin Geçici ÇözümÖzet - Generic Koleksiyon13Tasarım Kalıpları Bölüm 1
Tasarım Kalıplarına GirişSingleton KalıbıFactory KalıbıAbstract Factory KalıbıObserver KalıbıStrategy Kalıbı2Türler ve Struct'lar: Derinlemesine İnceleme
Temel ve Bileşik TürlerÖzel Tür TanımlamalarıStruct EtiketleriAnonim Struct'larİç İçe Geçmiş Struct'larSıfır Değerler ve VarsayılanlarÖzet - Rehber5Kalıtım Yerine Kompozisyon
Go'da Neden Kalıtım YokStruct Gömme TemelleriMetot YükseltmeBirden Fazla Struct GömmeGömme vs AgregasyonGömülü Metotları GölgelemeÖzet - Çalışan Hiyerarşisi8Hata Yönetimi ve OOP
error ArayüzüÖzel Hata TürleriHata Sarmalama (fmt.Errorf)Sentinel Hatalarerrors.Is() ve errors.As()Panic, Defer ve RecoverÖzet - Dosya Ayrıştırıcı3İşaretçiler ve Bellek
Go'da İşaretçi TemelleriStruct İşaretçileriDeğer ile Geçme ve Referans ile Geçmenew() FonksiyonuGo'da Garbage CollectionÖzet - Linked List Oluşturucu6Go'da Polimorfizm
Interface'ler ile PolimorfizmGo'da Duck TypingInterface Karşılama KurallarıPolimorfik KoleksiyonlarDependency InjectionÖzet - Payment Processor9Eşzamanlılık ve OOP
Goroutine TemelleriKanallar ve İletişimTamponlu ve Tamponsuz KanallarSelect İfadesisync.Mutex ve sync.RWMutexsync.WaitGroupThread-Safe Struct TasarımıÖzet - Worker Pool