Go'da Duck Typing
Coddy'nin GO Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası — ders 42 / 107.
Duck typing terimi şu deyişten gelir: "Eğer bir ördek gibi yürüyorsa ve bir ördek gibi vaklıyorsa, o zaman o bir ördektir." Go'da bu, bir türün bir arayüzü (interface) uyguladığını açıkça beyan etmesine gerek olmadığı, sadece doğru metotlara sahip olmasının yeterli olduğu anlamına gelir.
Şu arayüzü ve tamamen ilgisiz iki türü ele alalım:
type Quacker interface {
Quack() string
}
type Duck struct{}
func (d Duck) Quack() string { return "Quack!" }
type Robot struct{}
func (r Robot) Quack() string { return "Beep-quack!" }
Ne Duck ne de Robot tanımlarının hiçbir yerinde Quacker ifadesinden bahsetmez. Yine de her ikisi de sadece doğru imzaya sahip bir Quack() metoduna sahip oldukları için arayüzü (interface) karşılarlar:
func MakeItQuack(q Quacker) {
fmt.Println(q.Quack())
}
func main() {
MakeItQuack(Duck{}) // Vak!
MakeItQuack(Robot{}) // Bip-vak!
}
Bu örtük karşılama güçlüdür çünkü arayüzleri tipler zaten mevcut olduktan sonra tanımlamanıza olanak tanır. Harici paketlerdeki tiplerle eşleşen bir arayüzü, onların kaynak kodunu değiştirmeden oluşturabilirsiniz. Tiplerin sizin arayüzünüzden haberdar olması gerekmez; sadece doğru şekilde davranmaları yeterlidir.
Go'daki Duck typing, derleme zamanı tür güvenliğini korurken size dinamik dillerin esnekliğini sağlar. Derleyici, türlerin arayüz (interface) değerleri olarak kullanılmasına izin vermeden önce, bu türlerin gerekli metotlara gerçekten sahip olup olmadığını doğrular.
Görev
KolayÖrdek tiplemesini (duck typing) iş başında gösteren bir mesajlaşma sistemi kuralım. Hiçbiri açıkça bir arayüzü uyguladığını (implement) belirtmeden, ortak bir arayüz üzerinden mesaj gönderebilen tamamen ilgisiz türler oluşturacaksınız.
Kodunuzu üç dosya halinde düzenleyeceksiniz:
messengers.go: Aynı davranışı paylaşan üç ilgisiz struct oluşturun:Numberalanına sahipPhone—SendMessage(text string) stringmetodu"SMS to [Number]: [text]"değerini döndürür.Emailalanına sahipComputer—SendMessage(text string) stringmetodu"Email to [Email]: [text]"değerini döndürür.Namealanına sahipPigeon—SendMessage(text string) stringmetodu"[Name] carries: [text]"değerini döndürür.
sender.go:SendMessage(text string) stringmetodunu gerektiren birMessengerarayüzü (interface) tanımlayın. BirMessengerdilimi (slice) ve bir mesaj dizesi alan, ardından her bir haberci üzerindeSendMessageçağrılmasının sonucunu içeren bir dize dilimi döndürenBroadcastadlı bir fonksiyon oluşturun.main.go: Girdiden detayları okuyun, her haberci türünden birer tane oluşturun, bunları birMessengerdiliminde toplayın ve hepsinden bir mesaj göndermek içinBroadcastfonksiyonunu kullanın. Her sonucu kendi satırında yazdırın.
Aşağıdaki girdiler sağlanacaktır:
- Satır 1: Telefon numarası
- Satır 2: E-posta adresi
- Satır 3: Güvercin adı
- Satır 4: Yayınlanacak mesaj
Örneğin, "555-1234", "alice@mail.com", "Winston" ve "Hello World" verildiğinde, çıktınız şöyle olmalıdır:
SMS to 555-1234: Hello World
Email to alice@mail.com: Hello World
Winston carries: Hello WorldBuradaki temel nokta, Phone, Computer ve Pigeon türlerinin ortak hiçbir yanının olmamasıdır; bir temel türü paylaşmazlar veya Messenger arayüzünü açıkça uygulamazlar. Yine de hepsi aynı şekilde "vakvakladıkları" (eşleşen metoda sahip oldukları) için Go, hepsinin Messenger değerleri olarak kullanılmasına izin verir. İşte bu, ördek tiplemesidir (duck typing).
Kopya kağıdı
Go'da, duck typing (ördek tipleme), bir türün bir arayüzü uyguladığını açıkça belirtmesine gerek olmadığı anlamına gelir; sadece eşleşen imzalara sahip gerekli metotlara sahip olması yeterlidir.
Bir tür, arayüzün gerektirdiği tüm metotlara sahipse, o arayüzü örtük olarak karşılar:
type Quacker interface {
Quack() string
}
type Duck struct{}
func (d Duck) Quack() string { return "Quack!" }
type Robot struct{}
func (r Robot) Quack() string { return "Beep-quack!" }
Hem Duck hem de Robot, tanımlarında belirtilmemesine rağmen Quacker arayüzünü karşılar:
func MakeItQuack(q Quacker) {
fmt.Println(q.Quack())
}
func main() {
MakeItQuack(Duck{}) // Quack!
MakeItQuack(Robot{}) // Beep-quack!
}
Bu örtük karşılama, türler zaten mevcut olduktan sonra arayüzler tanımlamanıza ve hatta kaynak kodlarını değiştirmeden harici paketlerdeki türlerle eşleşen arayüzler oluşturmanıza olanak tanır. Derleyici, türlerin gerekli metotlara sahip olduğunu derleme zamanında doğrulayarak, esnekliği korurken tip güvenliği sağlar.
Kendin dene
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Telefon numarasını oku
scanner.Scan()
phoneNumber := scanner.Text()
// E-posta adresini oku
scanner.Scan()
email := scanner.Text()
// Güvercin adını oku
scanner.Scan()
pigeonName := scanner.Text()
// Yayınlanacak mesajı oku
scanner.Scan()
message := scanner.Text()
// TODO: Her haberci türünden (Phone, Computer, Pigeon) birer tane oluşturun
// TODO: Tüm habercileri bir Messenger dilimi (slice) içinde toplayın
// TODO: Mesajı tüm haberciler aracılığıyla göndermek için Broadcast kullanın
// TODO: Her sonucu kendi satırına yazdırın
// Değişkenleri kullanmak için yer tutucu (uygularken kaldırın)
_ = phoneNumber
_ = email
_ = pigeonName
_ = message
}
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