Interface'ler ile Polimorfizm
Coddy'nin GO Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası — ders 41 / 107.
Polimorfizm, farklı türlerin ortak bir arayüz aracılığıyla tek tip olarak ele alınmasına olanak tanır. Go'da bu, kalıtım veya sınıf hiyerarşileri olmadan tamamen arayüzler aracılığıyla gerçekleştirilir.
Bir fonksiyon parametre olarak bir arayüz (interface) türü kabul ettiğinde, bu arayüzü uygulayan herhangi bir somut tür içeri aktarılabilir. Fonksiyonun belirli türü bilmesine gerek yoktur; yalnızca arayüz tarafından tanımlanan davranışla ilgilenir:
type Speaker interface {
Speak() string
}
type Dog struct{ Name string }
func (d Dog) Speak() string { return "Woof!" }
type Cat struct{ Name string }
func (c Cat) Speak() string { return "Meow!" }
func MakeSound(s Speaker) {
fmt.Println(s.Speak())
}
Artık MakeSound, bir Speak() metoduna sahip olan her türle çalışır:
func main() {
dog := Dog{Name: "Rex"}
cat := Cat{Name: "Whiskers"}
MakeSound(dog) // Hav!
MakeSound(cat) // Miyav!
}
Aynı fonksiyon çağrısı, geçirilen gerçek türe bağlı olarak farklı davranışlar üretir. Bu, polimorfizmdir. MakeSound fonksiyonu bir kez yazılır ancak Speaker arayüzünü karşıladıkları sürece sınırsız sayıda türle çalışır.
Bu yaklaşım, kodunuzu esnek ve genişletilebilir tutar. Konuşan yeni bir tür eklemek, mevcut fonksiyonlarda hiçbir değişiklik gerektirmez; sadece arayüzü (interface) uygulayın, otomatik olarak çalışacaktır.
Görev
KolayPolimorfizmi iş başında gösteren bir araç açıklama sistemi kuralım. Bir arayüz (interface) aracılığıyla ortak bir davranışı paylaşan farklı araç türleri oluşturacak ve ardından herhangi bir araçla çalışan tek bir fonksiyon yazacaksınız.
Kodunuzu iki dosya halinde düzenleyeceksiniz:
vehicles.go: BirDescribe() stringmetodu gerektiren birDescriberarayüzü tanımlayın. Ardından, her biri bu arayüzü kendi yöntemiyle uygulayan üç araç türü oluşturun:BrandveModelalanlarına sahipCar—Describe()metoduCar: [Brand] [Model]değerini döndürür.BrandveEngineCC(int) alanlarına sahipMotorcycle—Describe()metoduMotorcycle: [Brand] [EngineCC]ccdeğerini döndürür.Typealanına sahip (örneğin "Mountain" veya "Road")Bicycle—Describe()metoduBicycle: [Type]değerini döndürür.
main.go: Herhangi birDescriberkabul eden veDescribe()çağrısının sonucunu yazdıranPrintDescriptionadlı bir fonksiyon oluşturun. Girdiden araç detaylarını okuyun, her araç türünden birer tane oluşturun ve aynı fonksiyonun üç farklı türle de çalıştığını göstermek için her biriniPrintDescriptionfonksiyonuna gönderin.
Aşağıdaki girdiler sağlanacaktır:
- Satır 1: Araba markası
- Satır 2: Araba modeli
- Satır 3: Motosiklet markası
- Satır 4: Motosiklet motor hacmi (tam sayı)
- Satır 5: Bisiklet türü
Örneğin, Toyota, Camry, Honda, 600 ve Mountain verildiğinde, çıktınız şu şekilde olmalıdır:
Car: Toyota Camry
Motorcycle: Honda 600cc
Bicycle: MountainPrintDescription fonksiyonunun bir Car, Motorcycle veya Bicycle alıp almadığını bilmesine gerek olmadığına dikkat edin; sadece Describe() metodunu çağırır ve her tür kendi benzersiz çıktısıyla yanıt verir. İşte polimorfizm budur: tek bir fonksiyon, birden fazla davranış.
Kopya kağıdı
Polimorfizm (çok biçimlilik), farklı türlerin ortak bir arayüz aracılığıyla tek tip olarak ele alınmasını sağlar. Go'da bu, kalıtım veya sınıf hiyerarşileri olmadan arayüzler (interfaces) aracılığıyla gerçekleştirilir.
Bir fonksiyon parametre olarak bir arayüz türünü kabul ettiğinde, bu arayüzü uygulayan herhangi bir somut tür içeri aktarılabilir:
type Speaker interface {
Speak() string
}
type Dog struct{ Name string }
func (d Dog) Speak() string { return "Woof!" }
type Cat struct{ Name string }
func (c Cat) Speak() string { return "Meow!" }
func MakeSound(s Speaker) {
fmt.Println(s.Speak())
}
Aynı fonksiyon farklı türlerle çalışır:
func main() {
dog := Dog{Name: "Rex"}
cat := Cat{Name: "Whiskers"}
MakeSound(dog) // Woof!
MakeSound(cat) // Meow!
}
Fonksiyon bir kez yazılır ancak arayüzü karşıladıkları sürece sınırsız sayıda türle çalışır. Yeni türler eklemek, mevcut fonksiyonlarda herhangi bir değişiklik gerektirmez; sadece arayüzü uygulamanız yeterlidir.
Kendin dene
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
// TODO: Herhangi bir Describer kabul eden ve Describe() metodunu
// çağırmanın sonucunu yazdıran PrintDescription adında bir fonksiyon oluşturun
func main() {
reader := bufio.NewReader(os.Stdin)
// Araba detaylarını oku
carBrand, _ := reader.ReadString('\n')
carBrand = strings.TrimSpace(carBrand)
carModel, _ := reader.ReadString('\n')
carModel = strings.TrimSpace(carModel)
// Motosiklet detaylarını oku
motoBrand, _ := reader.ReadString('\n')
motoBrand = strings.TrimSpace(motoBrand)
motoEngineStr, _ := reader.ReadString('\n')
motoEngineStr = strings.TrimSpace(motoEngineStr)
motoEngine, _ := strconv.Atoi(motoEngineStr)
// Bisiklet detaylarını oku
bicycleType, _ := reader.ReadString('\n')
bicycleType = strings.TrimSpace(bicycleType)
// TODO: Giriş değerlerini kullanarak bir Car, Motorcycle ve Bicycle oluşturun
// TODO: Polimorfizmi göstermek için her araç için PrintDescription'ı çağırın
fmt.Println("TODO: Print vehicle descriptions")
}
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