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 arayüz uyumluluğu için ördek tiplemesini kullansa da bir türün gerçekten bir arayüzü karşılayıp karşılamadığını belirleyen özel kurallar vardır. Bu kuralları anlamak, gözden kaçması zor hatalardan kaçınmanıza yardımcı olur.
En önemli kural pointer alıcılarıyla ilgilidir. Bir yöntem pointer alıcısıyla tanımlanmışsa, arayüzü yalnızca o türe işaret eden bir pointer karşılar; türün kendisi karşılamaz:
type Saver interface {
Save() string
}
type Document struct{ Name string }
func (d *Document) Save() string { // işaretçi alıcı
return "Saved: " + d.Name
}
func Process(s Saver) {
fmt.Println(s.Save())
}
Save() bir işaretçi alıcısına sahip olduğundan, yalnızca *Document Saver arayüzünü karşılar:
func main() {
doc := Document{Name: "report.txt"}
Process(&doc) // çalışır - pointer arayüzü karşılar
// Process(doc) // derleme hatası - value arayüzü karşılamaz
}
Ancak tersi daha esnektir. Bir yöntem değer alıcısına sahipse, hem değer hem de işaretçi türleri arayüzü karşılar. Go, değer alıcısı yöntemleri çağırırken işaretçilerin başvurusunu otomatik olarak kaldırır:
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, Go’nun bir işaretçiden her zaman bir değer elde edebilmesi (referansını kaldırarak), ancak bir değerden her zaman bir işaretçi elde edememesi (değer adreslenebilir olmayabilir) nedeniyle vardır. Arayüzlerle çalışırken bu kuralı akılda tutmak, kafa karıştırıcı derleyici hatalarını önler.
Görev
Kolayİşaretçi ve değer alıcılarının arayüz uyumluluğunu nasıl etkilediğini gösteren bir yapılandırma sistemi oluşturalım. Alıcı seçiminin değerlerin, işaretçilerin veya her ikisinin bir arayüzle kullanılıp kullanılamayacağını belirlediği türler oluşturacaksın.
Kodunu üç dosya arasında düzenleyeceksin:
config.go: İki yöntem gerektiren birConfigurablearayüzü tanımla:GetValue() stringveSetValue(string). Ardından iki yapılandırma türü oluştur:ReadOnlyConfigve birValuealanı:GetValue()için bir değer alıcısı kullan (Value değerini döndürür) veSetValue()için bir işaretçi alıcısı kullan (Value değerini günceller)Settingve birDataalanı: her iki yöntem için de değer alıcıları kullan (GetValue, Data değerini döndürür; SetValue ise hiçbir şeyi değiştirmeden yalnızca "Cannot modify" yazdırır)
processor.go: BirProcessConfigve yeni bir değer dizesi kabul edenConfigurableadlı bir işlev oluştur. Bu işlev,GetValue()kullanarak geçerli değeri yazdırmalı, yeni değerleSetValue()işlevini çağırmalı ve ardından değişiklikleri göstermek için değeri tekrar yazdırmalıdır.main.go: Yapılandırma ayrıntılarını girdiden oku ve arayüz uyumluluğu kurallarını göster:- Bir
ReadOnlyConfigoluştur veProcessConfigişlevine bir işaretçi gönder (SetValuebir işaretçi alıcısına sahip olduğu için gereklidir) - Bir
Settingoluştur veProcessConfigişlevine değeri doğrudan gönder (her iki yöntem de değer alıcılarına sahip olduğu için çalışır)
- Bir
Aşağıdaki girdiler sağlanacaktır:
- 1. satır: ReadOnlyConfig için başlangıç değeri
- 2. satır: ReadOnlyConfig için ayarlanacak yeni değer
- 3. satır: Setting için başlangıç değeri
- 4. satır: Setting için denenmek istenen yeni değer
ProcessConfig işlevin şu biçimde yazdırmalıdır:
Current: [value]
Current: [value after SetValue]Örneğin debug, production, localhost ve remote verildiğinde çıktın şu şekilde olmalıdır:
Current: debug
Current: production
Current: localhost
Cannot modify
Current: localhostReadOnlyConfig değerinin gerçekten değiştiğine (çünkü bir işaretçi gönderdiğimize), Setting değerinin ise değişmeden kaldığına (değer alıcısına sahip SetValue özgün değeri değiştiremez) dikkat et. Temel çıkarım şudur: ReadOnlyConfig değerleri tek başlarına Configurable arayüzünü karşılamaz; yalnızca işaretçiler karşılar. Setting değerleri ise tüm yöntemleri değer alıcılarını kullandığı için doğrudan çalışır.
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ğeriyle bir ReadOnlyConfig oluştur
// TODO: ProcessConfig'e bir POINTER geçir (gerekli çünkü SetValue pointer receiver'a sahip)
// TODO: settingInitial değeriyle bir Setting oluştur
// TODO: VALUE'yu doğrudan ProcessConfig'e geçir (çalışır çünkü her iki metod da value receiver'lara sahip)
// Kullanılmayan değişken hatalarını önlemek için bu değişkenleri kullan
_ = 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 PoolKendi başına pratik yap: Online Go derleyicisi