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 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.

challenge icon

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 bir Configurable arayüzü tanımla: GetValue() string ve SetValue(string). Ardından iki yapılandırma türü oluştur:
    • ReadOnlyConfig ve bir Value alanı: GetValue() için bir değer alıcısı kullan (Value değerini döndürür) ve SetValue() için bir işaretçi alıcısı kullan (Value değerini günceller)
    • Setting ve bir Data alanı: 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: Bir ProcessConfig ve yeni bir değer dizesi kabul eden Configurable adlı bir işlev oluştur. Bu işlev, GetValue() kullanarak geçerli değeri yazdırmalı, yeni değerle SetValue() 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:
    1. Bir ReadOnlyConfig oluştur ve ProcessConfig işlevine bir işaretçi gönder (SetValue bir işaretçi alıcısına sahip olduğu için gereklidir)
    2. Bir Setting oluştur ve ProcessConfig iş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)

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

ReadOnlyConfig 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
}
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

Kendi başına pratik yap: Online Go derleyicisi