Ders 01 / 13
Arayüz Kavramı
On altı bağın 4'ü bildirimde, 4'ü kullanım yerinde, 8'i değerin içinde kuruluyor: kursun bağlayıcı dağılımı burada üretiliyor. Aynı tip dört arayüzü karşılıyor ve kaynağında sıfır atıf var; küçük arayüz ilkesi burada bir öğüt değil, yöntem sayısı arttıkça karşılayan tip sayısının düşmesiyle ölçülen bir gözlem. Sınırlayıcı ölçüm: derleme zamanı uyum kanıtı aynı bağı bildirime taşıyor, kanıt yokken derleyici sessiz kalıyor.
İçindekiler
Go Temelleri kursu bir adın görünürlüğünü, bir bağımlılığın sürümünü ve bir biçim kuralının uygulanmasını kimin belirlediğini sorarak kapandı — üçünün de yanıtı dilin kendisiydi: dışa aktarma bir harfin büyüklüğüne, sürüm seçimi modül dosyasının yazdığı en düşük isteğe, biçim kuralı biçimlendiricinin kendisine bağlıydı. Bu ders aynı soruyu bir adım içeri taşıyor. Bir tip bir arayüzü karşıladığında, bunu söyleyen tek bir satır yoktur — ne arayüzün tanımında, ne tipin kendi kaynağında. Derleyici bu bağı biliyor, ama kaynağı okuyan biri onu göremiyor. Öyleyse bu bağ nerede kuruluyor?
Bu kurs bu soruyu on altı kez soruyor — arayüzlerde, hata değerlerinde, genelliklerde — ve her seferinde yanıtı üç yerden birine yazıyor: bağ kaynakta açıkça mı yazılı (bildirim), derleyici onu bir değerin kullanıldığı yerde mi kuruyor (kullanım yeri), yoksa bağ çalışma zamanında değerin kendisinde mi taşınıyor (değer). Bu ders bu üç yeri ilk kez sayıyor ve kursun geri kalanının atıf yapacağı sayıyı üretiyor.
Bağ Nerede Kuruluyor
Bir arayüz bir yöntem kümesi tanımlar ve kendi başına hiçbir şey yapmaz — ne bir alan taşır ne bir davranış üretir. Bir tip, o yöntem kümesinin tamamını kendi yöntemleriyle karşılıyorsa arayüzü karşılar. Karşılamanın örtük olması, tipin kaynağında bunu bildiren hiçbir sözcüğün geçmemesi demek. Bir tip, kendi yazarının hiç duymadığı bir arayüzü bile karşılayabilir; tek koşul yöntem imzalarının eşleşmesidir.
Karşılamanın kendisi hiçbir zaman kaynakta yazılı değildir. Ama bu karşılamayı doğrulayan bir kanıt kaynağa eklenebilir — o zaman bağ bildirime taşınır. Bu ayrım dersin son bölümünün konusu. Şimdilik üç sınıfın sözlü tanımı yeterli: hangi bağın kaynakta açıkça durduğunu, hangisinin derleyici tarafından kullanım anında kurulduğunu, hangisinin çalışma zamanında değerle birlikte taşındığını ölçüm saydırıyor; bu bilgiyi biz baştan biliyoruz.
Çerçeve: Bağı Sınıflayan Bir Gözlemci
Ölçüm, her bağı tek bir soruyla sınıflayan küçük bir gözlemciden geçiyor. Üç paket düzeyi
sayaç (bildirim, kullanimYeri, deger) her gözlemi bir sınıfa yazıyor; gozle tek giriş
noktası, ozet toplamı basıyor.
// bag.go — bir bag nerede kuruluyor: bildirimde mi, kullanim yerinde mi, degerin icinde mi
package main
import "fmt"
var bildirim, kullanimYeri, deger int
func gozle(ad, yer, kanit string) {
switch yer {
case "bildirim":
bildirim++
case "kullanim":
kullanimYeri++
default:
deger++
}
fmt.Printf("%-40s %-10s %s\n", ad, yer, kanit)
}
func ozet() {
fmt.Printf("bag: %d bildirim, %d kullanim yeri, %d deger\n", bildirim, kullanimYeri, deger)
}
AR1. gozle her satırı üç parçayla alıyor: bir ad, bir sınıf, bir kanıt. Kanıt sütunu
iddiayı doğrulayan gerçek değeri taşıyor — hangi satırın hangi sınıfa girdiği elde önceden
biliniyor, çerçeve bu kararı üretmiyor, yalnız saydırıyor. Sayaçların paket düzeyinde
tutulması, ozet çağrıldığında toplamın gözlemlerle her zaman tutarlı kalmasını sağlıyor.
On Altı Bağ
Bu çerçeve tek bir pakette on altı bağı sırayla soruyor. Ölçüm arayüz karşılamasıyla açılıyor, gömme ve derleme zamanı kanıtla sürüyor, boş arayüze konan bir değerin dinamik tipiyle, tip iddiasının ve tip anahtarının değere sorduğu soruyla, tüketici tarafında tanımlanan bir arayüzle, hata değerinin örtük karşılanmasıyla, nöbetçi bir hatanın kimliğiyle, sarmalanan bir hatanın taşıdığı bağlamla, ve son olarak genel bir fonksiyonun çıkardığı tip parametresiyle, bir tip kısıtının bildirimiyle, genel bir yapının örneklenmesiyle kapanıyor.
// olcum.go — on alti bag, uc yere bolunur
package main
import (
"errors"
"fmt"
)
type Yazar interface{ Yaz(m string) string }
type Kapatan interface{ Kapat() error }
// Kisa, tuketici tarafinda tanimlanan bir arayuz: ureten tip onu hic anmiyor.
type Kisa interface{ Yaz(m string) string }
// Gomulu, iki sozlesmeyi bildirimde birlestiriyor.
type Gomulu interface {
Yazar
Kapatan
}
type Dosya struct{ Ad string }
func (d Dosya) Yaz(m string) string { return d.Ad + ":" + m }
func (d Dosya) Kapat() error { return nil }
// Derleme zamani uyum kaniti: yazan taraf bunu bildirime koydu.
var _ Gomulu = Dosya{}
type Kayit struct{ Sayi int }
func (k *Kayit) Hata() string { return "kayit" }
var ErrYok = errors.New("bulunamadi")
type AlanHatasi struct{ Alan string }
func (a *AlanHatasi) Error() string { return "alan gecersiz: " + a.Alan }
func ara(bulundu bool) error {
if !bulundu {
return fmt.Errorf("arama basarisiz: %w", ErrYok)
}
return nil
}
func dogrula() error {
return fmt.Errorf("kayit dogrulanamadi: %w", &AlanHatasi{Alan: "ad"})
}
func ilki[T any](d []T) (T, bool) {
var sifir T
if len(d) == 0 {
return sifir, false
}
return d[0], true
}
type Sayisal interface{ ~int | ~float64 }
func topla[T Sayisal](d []T) T {
var t T
for _, e := range d {
t += e
}
return t
}
type Yigin[T any] struct{ ogeler []T }
func (y *Yigin[T]) Ekle(o T) { y.ogeler = append(y.ogeler, o) }
func (y *Yigin[T]) Tip() string {
var sifir T
return fmt.Sprintf("%T", sifir)
}
func main() {
fmt.Println("-- bag nerede kuruluyor --")
var y Yazar = Dosya{Ad: "not"}
gozle("tipin arayuzu karsilamasi", "kullanim", "kaynakta hicbir yerde yazili degil")
gozle("derleme zamani uyum kaniti", "bildirim", "var _ Gomulu = Dosya{}")
gozle("arayuz gomme", "bildirim", "Gomulu = Yazar + Kapatan")
gozle("arayuz degerinin dinamik tipi", "deger", fmt.Sprintf("%T", y))
d, tamam := y.(Kapatan)
gozle("tip iddiasinin yaniti", "deger", fmt.Sprint(tamam, " ", d != nil))
var tur string
switch y.(type) {
case Dosya:
tur = "Dosya"
default:
tur = "bilinmeyen"
}
gozle("tip anahtarinin sectigi dal", "deger", tur)
var _ Kisa = Dosya{}
gozle("arayuzun tuketici tarafinda tanimlanmasi", "kullanim",
"Dosya dort arayuzu karsiliyor, kaynaginda sifir atif var")
var bosArayuz any = y
gozle("bos arayuze konan degerin tipi", "deger", fmt.Sprintf("%T", bosArayuz))
var k *Kayit
var h any = k
gozle("nil isaretci tasiyan arayuz", "deger", fmt.Sprint("isaretci nil=", k == nil, " arayuz nil=", h == nil))
var hata error = &AlanHatasi{Alan: "ad"}
gozle("hata arayuzunun karsilanmasi", "kullanim", fmt.Sprintf("%T", hata))
sarmali := ara(false)
gozle("nobetci hatanin kimligi", "deger", fmt.Sprint(errors.Is(sarmali, ErrYok)))
gozle("sarmalamanin tasidigi metin", "deger", sarmali.Error())
var hedef *AlanHatasi
gozle("sarmali zincirden tip cikarma", "deger", fmt.Sprint(errors.As(dogrula(), &hedef), " ", hedef.Alan))
ilk, varMi := ilki([]string{"a", "b"})
gozle("genel fonksiyonun tip cikarimi", "kullanim", fmt.Sprint(ilk, " ", varMi))
gozle("tip kisitinin bildirimi", "bildirim", fmt.Sprint("Sayisal ile toplam=", topla([]int{1, 2, 3})))
var yigin Yigin[float64]
yigin.Ekle(1.5)
gozle("genel tipin orneklenmesi", "bildirim", "Yigin[float64] -> "+yigin.Tip())
ozet()
}
-- bag nerede kuruluyor --
tipin arayuzu karsilamasi kullanim kaynakta hicbir yerde yazili degil
derleme zamani uyum kaniti bildirim var _ Gomulu = Dosya{}
arayuz gomme bildirim Gomulu = Yazar + Kapatan
arayuz degerinin dinamik tipi deger main.Dosya
tip iddiasinin yaniti deger true true
tip anahtarinin sectigi dal deger Dosya
arayuzun tuketici tarafinda tanimlanmasi kullanim Dosya dort arayuzu karsiliyor, kaynaginda sifir atif var
bos arayuze konan degerin tipi deger main.Dosya
nil isaretci tasiyan arayuz deger isaretci nil=true arayuz nil=false
hata arayuzunun karsilanmasi kullanim *main.AlanHatasi
nobetci hatanin kimligi deger true
sarmalamanin tasidigi metin deger arama basarisiz: bulunamadi
sarmali zincirden tip cikarma deger true ad
genel fonksiyonun tip cikarimi kullanim a true
tip kisitinin bildirimi bildirim Sayisal ile toplam=6
genel tipin orneklenmesi bildirim Yigin[float64] -> float64
bag: 4 bildirim, 4 kullanim yeri, 8 deger
On altı bağın 4’ü bildirimde, 4’ü kullanım yerinde, 8’i değerin içinde kuruluyor. Bu dağılım kursun geri kalanı için bağlayıcı; sonraki dersler kendi diliminin satırlarını basıp bu tabloya tek cümleyle atıf yapacak. Sekizinin değerde kurulması Go’nun arayüz değerinin bir çift olmasından geliyor — içinde hem somut tip hem değer duruyor; tip iddiası, tip anahtarı, nöbetçi hata karşılaştırması ve sarmalı zincirden tip çıkarma dördü de aynı çiftin okunması. Bu derste asıl konu ilk iki satır ve yedinci satır: arayüz karşılamasının kendisi ve onun tüketici tarafında tanımlanması.
Örtük Gerçekleştirim: Sıfır Atıflı Dört Arayüz
Dosya tipinin kaynağı iki satırdan ibaret: bir alan, iki yöntem (Yaz, Kapat). Bu iki
satır dört ayrı arayüzü karşılıyor — Yazar (Yaz yöntemini istiyor), Kapatan (Kapat
yöntemini istiyor), Gomulu (ikisini birden gömerek istiyor) ve Kisa (yalnız Yaz
istiyor, Yazarla aynı imzayı taşıyan ayrı bir tanım). Dosyanın kendi kaynağında bu dört
arayüzün adı hiç geçmiyor — ne type Dosya struct satırında, ne Yaz ve Kapat
yöntemlerinin gövdesinde. Kaynağa sıfır atıf var, karşılama sayısı dört.
Bu, örtük gerçekleştirimin somut sonucu: bir tip, kendi yazarının hiç görmediği bir arayüzü
bile karşılayabiliyor, çünkü karşılamanın koşulu yalnızca yöntem imzalarının eşleşmesi. Kisa
arayüzü olcum.go içinde Yazardan sonra tanımlandı ve Dosya ondan habersiz yazıldı;
buna rağmen var _ Kisa = Dosya{} satırı derleniyor. Tablodaki yedinci satır bunu bir sayıya
indiriyor: dört arayüz, sıfır atıf. Bu gözlem beşinci dersin konusu olan tüketici tarafında
tanımlamanın ilk görünümü — orada aynı fikir bir tasarım kararına dönüşecek.
Küçük Arayüz İlkesi: Öğüt Değil Ölçüm
Bir arayüzün yöntem sayısı arttıkça, onu karşılayan tip sayısının düşmesi beklenir — daha çok yöntem istemek daha sıkı bir koşul demek. Bu, Tasarım İlkeleri kursunun arayüz ayrımı ilkesinin dayandığı sezgi; burada bir öğüt olarak değil, sayılabilir bir ilişki olarak ölçülüyor.
// kucuk-arayuz.go — arayuzun yontem sayisi arttikca onu karsilayan tip sayisi azaliyor mu
package main
import "fmt"
type BirYontemli interface{ Oku() string }
type IkiYontemli interface {
Oku() string
Yaz(m string)
}
type UcYontemli interface {
Oku() string
Yaz(m string)
Kapat() error
}
type SadeceOkuyan struct{}
func (SadeceOkuyan) Oku() string { return "oku" }
type OkuyupYazan struct{}
func (OkuyupYazan) Oku() string { return "oku" }
func (OkuyupYazan) Yaz(m string) {}
type OkuyupYazipKapatan struct{}
func (OkuyupYazipKapatan) Oku() string { return "oku" }
func (OkuyupYazipKapatan) Yaz(m string) {}
func (OkuyupYazipKapatan) Kapat() error { return nil }
type SadeceYazan struct{}
func (SadeceYazan) Yaz(m string) {}
func main() {
tipler := []any{SadeceOkuyan{}, OkuyupYazan{}, OkuyupYazipKapatan{}, SadeceYazan{}}
birSayisi, ikiSayisi, ucSayisi := 0, 0, 0
for _, t := range tipler {
if _, tamam := t.(BirYontemli); tamam {
birSayisi++
}
if _, tamam := t.(IkiYontemli); tamam {
ikiSayisi++
}
if _, tamam := t.(UcYontemli); tamam {
ucSayisi++
}
}
fmt.Println("bir yontemli arayuzu karsilayan tip sayisi:", birSayisi)
fmt.Println("iki yontemli arayuzu karsilayan tip sayisi:", ikiSayisi)
fmt.Println("uc yontemli arayuzu karsilayan tip sayisi :", ucSayisi)
}
bir yontemli arayuzu karsilayan tip sayisi: 3 iki yontemli arayuzu karsilayan tip sayisi: 2 uc yontemli arayuzu karsilayan tip sayisi : 1
AR2. Dört tip bilinçli olarak kuruldu: üçü Okuyu artan yöntem kümeleriyle taşıyor,
dördüncüsü (SadeceYazan) hiç Oku taşımıyor ve üç arayüzün de dışında kalıyor. Bu son tip
olmasaydı düşüş yine görülürdü ama “arayüzü hiç karşılamayan bir tip de var” gözlemi
kaybolurdu — ölçüm bu yüzden dördü birlikte tutuyor.
Dört tip, üç arayüz karşısında sınandığında sayı 3, 2, 1 olarak düşüyor: yöntem kümesi bir yöntemden üçe çıktıkça, aynı dört tipten onu karşılayabilenlerin sayısı azalıyor. Bu bağ tabloda görülen “kullanım yeri” sınıfının aynısı — her sayım, bir tip iddiasının çağrıldığı satırda kuruluyor, tipin ya da arayüzün kendi tanımında değil. Küçük arayüz, az yöntem isteyen arayüz demek; az yöntem isteyen arayüz, çoğu tipin karşıladığı arayüz demek. Bu bir tasarım öğüdü değil, yukarıdaki sayımın doğrudan sonucu.
Sınırlayıcı Ölçüm: Uyum Kaynakta da Yazılabilir
On altı bağın ilk satırı “kullanım yeri” diyordu ve kanıtı dolaylıydı: karşılamanın kaynakta
yazılı olmadığını gösterdi, kurulduğu yeri değil. Bu bölüm doğrudan kanıtı veriyor: uyumu
kasten bozup derleyicinin nerede durduğunu gösteriyor. Araç, Go Temelleri kursunun eksik.go
çerçevesindeki derlenirMi — bir kaynak parçasını geçici bir pakete yazıp derleyiciyi çağırıyor
ve yalnız geçip geçmediğini okuyor.
// arac.go — bir kaynak parcasinin derlenip derlenmedigini programin icinden sinar
package main
import (
"os"
"os/exec"
"path/filepath"
)
func derlenirMi(kaynak string) bool {
dizin, err := os.MkdirTemp("", "sinama")
if err != nil {
return false
}
defer os.RemoveAll(dizin)
os.WriteFile(filepath.Join(dizin, "go.mod"), []byte("module sinama\n\ngo 1.24\n"), 0o644)
os.WriteFile(filepath.Join(dizin, "kod.go"), []byte(kaynak), 0o644)
cmd := exec.Command("go", "build", "./...")
cmd.Dir = dizin
cmd.Env = append(os.Environ(), "GOTOOLCHAIN=local")
return cmd.Run() == nil
}
Bu iki kaynak parçası aynı yarım gerçekleştirimi taşıyor — bir tip, iki yöntemli bir arayüzün yalnız birini yazıyor. Fark tek satır: ikincisinde tipin hemen ardından bir uyum kanıtı var.
// uyum-kaniti.go — kanit yokken derleyici sessiz kaliyor, kanit varsa bildirimde duruyor
package main
import "fmt"
func main() {
kaynakA := `package sinama
type TamArayuz interface {
Birinci() string
Ikinci() string
}
type YarimGerceklestirim struct{}
func (y YarimGerceklestirim) Birinci() string { return "birinci" }
`
kaynakB := kaynakA + `
var _ TamArayuz = YarimGerceklestirim{}
`
fmt.Println("kanitsiz, hic kullanilmadan :", derlenirMi(kaynakA))
fmt.Println("kanitla, tanimin hemen ardinda:", derlenirMi(kaynakB))
}
kanitsiz, hic kullanilmadan : true kanitla, tanimin hemen ardinda: false
YarimGerceklestirim iki kaynakta da aynı — Ikinci yöntemi eksik. Birinci kaynakta bu tip
hiçbir yerde TamArayuz olarak kullanılmıyor; derleyici sessiz kalıyor ve eksik yöntem hiçbir
uyarı üretmiyor. İkinci kaynakta tek eklenen satır, tipin tanımının hemen ardına konan bir
uyum kanıtı; aynı eksiklik şimdi derlemeyi durduruyor. Bağ ikinci kaynakta bildirime
taşınmış oldu — kanıt satırı, karşılamanın kendisini değil, karşılamanın doğrulamasını
kaynağa yazıyor, ve bu doğrulama bozulduğunda derleme orada duruyor.
AR3. Bu ölçümün bir kör noktası var: derlenirMi yalnız cmd.Run()ın çıkış durumunu
okuyor, hata metnini hiç okumuyor. Birinci kaynağın gerçekten başka bir nedenle değil,
yalnız eksik yöntem yüzünden derlendiğinden emin olmak elle kuruldu — kaynak, Ikinci
dışında hiçbir sorun taşımayacak şekilde yazıldı. Kursun derleyici çağıran her ölçümünde aynı
disiplin sürüyor.
Uyum kanıtı olmadan bırakılan bir tip, hiç kullanılmadığı sürece eksikliğini asla göstermez. Bu, “kullanım yerinde kuruluyor” demenin gerçek maliyeti: bağ yalnız kullanıldığı anda sınanıyor, kullanılmazsa hiç sınanmıyor. Bir kanıt satırı bu riski, tipin tanımının yanına taşıyarak kapatıyor.
Özet
- Bir tipin bir arayüzü karşılaması örtüktür: bunu bildiren hiçbir sözcük kaynakta geçmez; tek koşul yöntem imzalarının eşleşmesidir.
- On altı bağın 4’ü bildirimde, 4’ü kullanım yerinde, 8’i değerin içinde kuruluyor — kursun bağlayıcı dağılımı bu derste üretildi.
Dosyatipi dört arayüzü (Yazar,Kapatan,Gomulu,Kisa) karşılıyor ve kendi kaynağında bu dördüne sıfır atıf var.- Küçük arayüz ilkesi bir öğüt değil ölçüm: yöntem sayısı bir–iki–üçe çıktıkça, aynı dört tipten onu karşılayabilenlerin sayısı üç–iki–bire düşüyor.
- Derleme zamanı uyum kanıtı (
var _ Arayuz = Tip{}) bağı bildirime taşıyor; kanıt yokken eksik bir gerçekleştirim, kullanılmadığı sürece hiç görünmüyor.
Sonraki Adım
Bu derste arayüz değerinin dinamik tipi bir kez göründü — %T ile okunan main.Dosya — ama
üzerinde durulmadı. Sıradaki ders tam olarak bu satırı büyütüyor: hiçbir yöntem istemeyen boş
arayüz, hangi tip konursa konsun onu kabul ediyor, ama konan tipin bilgisi kaybolmuyor. O
bilgi nereye gidiyor, ve %T onu nasıl geri okuyor?
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.