Menu
Coddy logo textTech

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.

challenge icon

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 bir Configurable arayüzü tanımlayın: GetValue() string ve SetValue(string). Ardından iki yapılandırma türü oluşturun:
    • Value alanına sahip ReadOnlyConfigGetValue() için bir değer alıcısı (Value değerini döndürür) ve SetValue() için bir işaretçi alıcısı (Value değerini günceller) kullanın.
    • Data alanına sahip Setting—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: Bir Configurable ve yeni bir değer dizesi (string) kabul eden ProcessConfig adında bir fonksiyon oluşturun. Bu fonksiyon, GetValue() kullanarak mevcut değeri yazdırmalı, yeni değerle SetValue() 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:
    1. Bir ReadOnlyConfig oluşturun ve ProcessConfig fonksiyonuna bir işaretçi (pointer) geçirin (bu gereklidir çünkü SetValue bir işaretçi alıcısına sahiptir).
    2. Bir Setting oluşturun ve değeri doğrudan ProcessConfig fonksiyonuna geçirin (bu çalışır çünkü her iki metot da değer alıcılarına sahiptir).

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: localhost

ReadOnlyConfig'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şılar

Bu 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
}
quiz iconKendini test et

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