---
title: 'Arayüz Tasarımı'
source: 'https://academia.sh/tr/kurslar/go-tip-sistemi/arayuz-tasarimi'
course: 'Arayüzler, Hatalar ve Genellikler'
language: tr
updated: '2026-08-23T14:25:02+00:00'
license: 'CC BY-SA 4.0'
---

# Arayüz Tasarımı

Tüketici tarafında tanımlama ölçülüyor: dört yöntemli bir tip hiçbir arayüze atıf yapmadan yazılıyor, dört ayrı tüketici birbirinden habersiz kendi tek yöntemli arayüzünü tanımlıyor ve aynı tip dördünü de karşılıyor — üreten tarafın kaynağında bu dört adın hiçbirine atıf yok. Sınırlayıcı ölçüm: aynı sözleşme iki tüketicide iki ayrı adla tanımlandığında birbirinin yerine geçemez görünüyor, ama tip sistemi ikisini de aynı değere doğrudan kabul ediyor.

Önceki ders, sözleşmeyi tanımlayan tarafın sözleşmeyi karşılayacak tipten **önce**
davrandığı bir örnekle açıldı: `Okuyucu` ve `Yazici`, `Dosya` yazılmadan önce zaten
oradaydı. Ama kursun ilk dersindeki `Kisa`, üçüncü dersindeki `Depolayici` tam tersini
yaptı — ikisi de karşıladıkları tipin kaynağında hiç geçmedi. Bu ders o gözlemi bir
tasarım kararına dönüştürüyor: bir arayüzü kim tanımlamalı, üreten taraf mı, tüketen taraf
mı, ve bu seçimin ölçülebilir bir karşılığı var mı?

Bu soru bir tercih değil, birinci dersin ikinci iddiasının doğal uzantısı. Kaynak, bir
tipin bir arayüzü karşıladığını hiç söylemiyorsa, o arayüzün üretici tarafın kaynağında
**bulunması gerekmiyor** demektir — tüketici, üreticinin hiç bilmediği bir sözleşmeyi
sonradan yazıp aynı tipe uygulayabiliyor. Bu ders bu imkânı bir kez daha, bu kez ölçekte
ve bir bedelle birlikte gösteriyor: tek bir tip, birden çok bağımsız tüketici tarafından
kaç ayrı sözleşmeyle sarılabiliyor, ve bu esnekliğin karşılığında hangi görünmezlik
ortaya çıkıyor.

## Tüketici Tarafında Tanımlama

Aşağıdaki ölçüm, dört yöntemli bir tipi (`Depo`) hiçbir arayüze atıf yapmadan yazıyor.
Ardından dört ayrı tüketici, birbirinden habersiz, kendi tek yöntemli arayüzünü
tanımlıyor. Ölçüm, `Depo`nun bu dört arayüzden kaçını karşıladığını sayıyor.

```go
// depo.go — uretici tarafin kodu, hicbir arayuze atif yapmadan yaziliyor
package main

import "fmt"

type Depo struct{ ogeler []string }

func (d *Depo) Ekle(o string)     { d.ogeler = append(d.ogeler, o) }
func (d *Depo) Sil(o string) bool { return true }
func (d *Depo) Listele() []string { return d.ogeler }
func (d *Depo) Boyut() int        { return len(d.ogeler) }

// Dort ayri tuketicinin kendi tanimladigi, birbirinden habersiz dort arayuz.
type Yazici interface{ Ekle(o string) }
type Temizleyici interface{ Sil(o string) bool }
type Raporlayici interface{ Listele() []string }
type Olculebilir interface{ Boyut() int }
type Yedekleyici interface{ Yedekle() error }

func main() {
	var d any = &Depo{}
	_, yaziciMi := d.(Yazici)
	_, temizleyiciMi := d.(Temizleyici)
	_, raporlayiciMi := d.(Raporlayici)
	_, olculebilirMi := d.(Olculebilir)
	_, yedekleyiciMi := d.(Yedekleyici)

	sayisi := 0
	for _, tamam := range []bool{yaziciMi, temizleyiciMi, raporlayiciMi, olculebilirMi, yedekleyiciMi} {
		if tamam {
			sayisi++
		}
	}
	fmt.Println("Yazici mi      :", yaziciMi)
	fmt.Println("Temizleyici mi :", temizleyiciMi)
	fmt.Println("Raporlayici mi :", raporlayiciMi)
	fmt.Println("Olculebilir mi :", olculebilirMi)
	fmt.Println("Yedekleyici mi :", yedekleyiciMi)
	fmt.Println("bes adaydan karsilanan sayisi:", sayisi)
}
```

```
Yazici mi      : true
Temizleyici mi : true
Raporlayici mi : true
Olculebilir mi : true
Yedekleyici mi : false
bes adaydan karsilanan sayisi: 4
```

`Depo`nun kaynağı dört satırdan ibaret: `Ekle`, `Sil`, `Listele`, `Boyut`. Bu dört satırda
`Yazici`, `Temizleyici`, `Raporlayici`, `Olculebilir` adlarının hiçbiri geçmiyor — `Depo`
yazılırken bu dört arayüz henüz var olmayabilirdi bile. Buna rağmen `Depo` dördünü de
karşılıyor, çünkü karşılamanın koşulu yalnızca yöntem imzalarının eşleşmesi. Beşinci aday
(`Yedekleyici`) bilinçli olarak eklendi: `Depo`nun yazmadığı bir yöntem (`Yedekle`)
istiyor ve `false` dönüyor — ölçüm yalnız uyanları değil, uymayanı da gösteriyor, ki
sonuç seçici bir örnekleme değil gerçek bir sınır olsun.

Dört arayüzün her biri farklı bir tüketici senaryosunu temsil ediyor: `Yazici` yalnızca
yeni öge ekleyen bir kod parçasının ihtiyacı, `Temizleyici` yalnızca silme yapan bir
işlevin, `Raporlayici` yalnızca listeleme yapan bir görünümün, `Olculebilir` yalnızca
boyut sınayan bir denetimin. Bu dört tüketici birbirini hiç görmemiş olabilir, ayrı
paketlerde, ayrı ekipler tarafından yazılmış olabilir — tek ortak noktaları `Depo`nun
kendisi. Yine de dördü de kendi dar sözleşmesini kurup `Depo`yu ona uydurabiliyor, çünkü
uyum kontrolü merkezî bir kayıt yerine her tüketicinin kendi bakış açısından yapılıyor.

**AR13.** Dört tüketici arayüzü tek bir yöntem istiyor; bu bilinçli bir seçim. Kursun
ilk dersindeki küçük arayüz ölçümü, yöntem sayısı arttıkça karşılayan tip sayısının
düştüğünü göstermişti. Burada tersi kullanılıyor: yöntem sayısını bire indirmek,
`Depo`nun tek bir yöntemi olan her tüketiciyi karşılamasını neredeyse garantiliyor —
tüketici tarafında tanımlamanın pratikte bu kadar sık işlemesinin bir nedeni de bu.

Bu düzende üreten paket bir kez yazılıyor ve hiç değişmiyor; her yeni tüketici kendi
arayüzünü kendi ihtiyacına göre tanımlıyor. Kursun terminolojisinde bu, arayüzün
karşılanmasının bağını **kullanım yerine** taşıyan aynı mekanizma — birinci dersteki
`Kisa`nın ve üçüncü dersteki `Depolayici`nin yaptığı da tam olarak buydu. Fark, bu
dersin onu tek bir örnekten çıkarıp bir tasarım gözlemine dönüştürmesi: dört tüketici,
dört farklı ihtiyaç, dört farklı ad, ve üretici tarafın bunların hiçbirinden haberi yok.
Tasarım İlkeleri kursunun arayüz ayrımı ilkesi bu deseni bir öğüt olarak sunuyor; bu ders
öğüt vermiyor, deseni sayıyor.

Sayının kendisi de bir şey söylüyor: `Depo` dört arayüzü karşılıyor, ama `Depo`nun kendi
kaynağında dört değil **sıfır** arayüz adı geçiyor. `Depo`nun kaynağı hiç değişmeden,
`Depo`nun mevcut dört yönteminin herhangi bir alt kümesini isteyen yeni bir arayüz her an
tanımlanabilir ve `Depo` onu da karşılamaya devam eder — beşinci, altıncı, onuncu
tüketici için de aynı kural geçerli, üretici tarafın kaynağının bundan haberi olması
gerekmiyor. `Depo`yu yazan taraf, kaç tüketicinin kendisini kaç farklı sözleşmeyle
sardığını bilmek zorunda değil.

## Tüketicinin Kendi Bileşiği

Dördüncü ders gömmenin sözleşmeleri **üreten tarafta** birleştirdiğini ölçmüştü:
`Okuyucu` ve `Yazici`, `Dosya` yazılmadan önce zaten `Gomulu` içinde birleşmişti. Aynı
birleşme tüketici tarafında da mümkün — yalnız bu kez birleştiren taraf üretici değil,
tüketicinin kendisi.

```go
// bilesik-tuketici.go — tuketici, uretici tipin dort yontemini kendi sozlesmesinde topluyor
package main

import "fmt"

type Depo struct{ ogeler []string }

func (d *Depo) Ekle(o string)     { d.ogeler = append(d.ogeler, o) }
func (d *Depo) Sil(o string) bool { return true }
func (d *Depo) Listele() []string { return d.ogeler }
func (d *Depo) Boyut() int        { return len(d.ogeler) }

type Yazici interface{ Ekle(o string) }

// Besinci tuketici, dort yontemi kendi bilesik sozlesmesinde topluyor; gomme kullanmiyor,
// dort imzayi elle yaziyor.
type TamDepo interface {
	Ekle(o string)
	Sil(o string) bool
	Listele() []string
	Boyut() int
}

func main() {
	var d any = &Depo{}
	_, yaziciMi := d.(Yazici)
	_, tamDepoMu := d.(TamDepo)
	fmt.Println("dar sozlesme,  Yazici mi   :", yaziciMi)
	fmt.Println("genis sozlesme, TamDepo mu  :", tamDepoMu)
}
```

```
dar sozlesme,  Yazici mi   : true
genis sozlesme, TamDepo mu  : true
```

`TamDepo`, `Depo`nun dört yönteminin tamamını tek bir sözleşmede topluyor, ama bunu
gömerek değil, dördünü tek tek yazarak yapıyor — `TamDepo` kendi başına bir gömme
zinciri değil. Buna rağmen `Depo` onu da karşılıyor, tıpkı tek yöntemli `Yazici`yi
karşıladığı gibi. Dördüncü dersteki ölçüm gömmenin **üretici tarafın** sözleşmesini nasıl
birleştirdiğini gösterdi; bu ölçüm aynı birleşmenin **tüketici tarafında**, üreticinin hiç
haberi olmadan da kurulabildiğini gösteriyor. İki model burada çakışmıyor, aynı tipte yan
yana duruyor: `Depo` hem en dar sözleşmeyi (`Yazici`) hem en geniş sözleşmeyi (`TamDepo`)
aynı anda karşılıyor, çünkü ikisi de aynı dört yöntemin farklı alt kümelerini istiyor.

**AR14.** `TamDepo`nun `Depo`nun tam olarak yazdığı dört yöntemi istemesi bilinçli bir
seçim — sözleşme üreticinin yöntem kümesini aşsaydı, `Depo` onu karşılamazdı. Ölçümün
gösterdiği şey sınırsız bir uyum değil: tüketici ne kadar geniş bir sözleşme yazarsa
yazsın, üreticinin gerçekte yazdığı yöntemlerin dışına çıkamıyor.

Bu, tüketici tarafında tanımlamanın gerçek sınırını netleştiriyor: tüketici, üreticinin
**neyi yazdığını** seçemiyor, yalnızca üreticinin yazdıklarından **hangisini istediğini**
seçebiliyor. `TamDepo` beşinci bir yöntem (`Yedekle`) isteseydi, dördüncü bölümdeki
`Yedekleyici` gibi karşılanmazdı — geniş bir sözleşme yazmak, üretici tarafın kaynağına
yeni bir yöntem eklemenin yerine geçmiyor. Tüketici tarafında tanımlamanın verdiği
özgürlük, var olan yöntemlerin **seçimi** üzerinedir, var olmayan yöntemlerin
**icadı** üzerinde değil.

## Sınırlayıcı Ölçüm: Aynı Sözleşme, İki Ayrı Ad

Tüketici tarafında tanımlama bedelsiz değil. İki ayrı tüketici aynı yöntemi isteyen iki
ayrı arayüz tanımladığında, bu iki arayüz kaynakta hiçbir ortak noktaya sahip görünmüyor
— ikisi de kendi paketinde, kendi adıyla, birbirinden habersiz duruyor. Ama Go'nun tip
sistemi arayüzleri **adına göre değil yöntem kümesine göre** karşılaştırıyor; bu yüzden
iki ayrı ad, aynı imzayı taşıdığı sürece birbirinin yerine geçiyor.

```go
// iki-ad.go — ayni imzayi tasiyan iki ayri arayuz adi, birbirinin yerine geciyor
package main

import "fmt"

type Depo struct{ ogeler []string }

func (d *Depo) Ekle(o string) { d.ogeler = append(d.ogeler, o) }

// Iki tuketicinin birbirinden habersiz tanimladigi, ayni imzali iki arayuz.
type Yazici interface{ Ekle(o string) }
type Girdi interface{ Ekle(o string) }

func birinciTuketici(y Yazici) string { y.Ekle("A"); return "Yazici uzerinden eklendi" }
func ikinciTuketici(g Girdi) string   { g.Ekle("B"); return "Girdi uzerinden eklendi" }

func main() {
	d := &Depo{}

	var g Girdi = d
	var y Yazici = g // Girdi tipindeki bir deger dogrudan Yazici degiskenine atanabiliyor mu
	fmt.Println("Girdi degeri Yazici degiskenine atanabildi mi:", y != nil)

	fmt.Println(birinciTuketici(g)) // Girdi degeri Yazici parametresine geciyor
	fmt.Println(ikinciTuketici(y))  // Yazici degeri Girdi parametresine geciyor
	fmt.Println("toplam eklenen oge sayisi:", len(d.ogeler))
}
```

```
Girdi degeri Yazici degiskenine atanabildi mi: true
Yazici uzerinden eklendi
Girdi uzerinden eklendi
toplam eklenen oge sayisi: 2
```

`Yazici` ile `Girdi`, iki ayrı ad taşıyan, birbirinden habersiz yazılmış iki ayrı arayüz.
Aralarında hiçbir gömme, hiçbir ortak tanım yok. Buna rağmen `var y Yazici = g` satırı
derleniyor — bir `Girdi` değeri doğrudan bir `Yazici` değişkenine atanabiliyor — ve
`birinciTuketici`, `Yazici` beklerken bir `Girdi` değeri kabul ediyor. Fark yalnızca adda;
Go'nun tip sistemi arayüzleri isimleriyle değil, istedikleri yöntemlerle tanımlıyor. İki
farklı tüketici aynı yöntemi isteyen iki farklı arayüz tanımladığında, bu iki tanım
istemeden **eşdeğer** oluyor.

**AR15.** Bu satır kaynaktan okunarak tahmin edilemez: `Yazici` ve `Girdi` tanımlarına
tek tek bakan biri, aralarında bir ilişki olduğunu göremiyor, çünkü ilişki hiçbir yerde
yazılı değil — yalnızca ikisinin yöntem kümesi karşılaştırıldığında ortaya çıkıyor.
Ölçüm bu köru noktayı doğrudan koşturarak gösteriyor; kaynağı okumak yetmiyor, iki
tanımı yan yana koyup imzaları karşılaştırmak gerekiyor.

Bu eşdeğerliğin sınırı da net: imzalar **birebir** aynı olmak zorunda, yalnız benzemesi
yetmiyor. Aşağıdaki ölçüm bunu doğrudan derleyiciye soruyor.

```go
// sinir.go — ayni imza gecerli kiliyor, farkli donus tipi reddediyor
package main

import (
	"fmt"
	"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
}

func main() {
	ortak := `package sinama

type Yazici interface{ Ekle(o string) }

func f(y Yazici) {}

`
	ayniImza := ortak + `type Girdi interface{ Ekle(o string) }

func g(x Girdi) { f(x) }
`
	farkliImza := ortak + `type Aktarici interface{ Ekle(o string) error }

func g(x Aktarici) { f(x) }
`
	fmt.Println("ayni imzali arayuz gecerli mi  :", derlenirMi(ayniImza))
	fmt.Println("farkli imzali arayuz gecerli mi:", derlenirMi(farkliImza))
}
```

```
ayni imzali arayuz gecerli mi  : true
farkli imzali arayuz gecerli mi: false
```

`Girdi`, `Yazici` ile birebir aynı imzayı (`Ekle(string)`) taşıdığı için `Yazici`
beklenen yerde kabul ediliyor. `Aktarici` ise aynı adı (`Ekle`) taşısa da farklı bir
dönüş tipi (`error`) istiyor, ve bu fark tek başına eşdeğerliği bozmaya yetiyor —
derleniyor mu sorusu `false`e dönüyor. Eşdeğerlik ada değil, imzanın **her parçasına**
bakıyor: yöntem adı, parametre tipleri, dönüş tipleri, hepsi birden eşleşmek zorunda.
İki tüketicinin bağımsız yazdığı arayüzlerin örtük olarak eşleşmesi, ancak ikisi de
aynı sözleşmeyi harfiyen tekrarladığında oluyor; küçük bir fark bile bu örtük bağı
koparıyor.

Bu, tüketici tarafında tanımlamanın maliyetidir: bağımsız tüketiciler bağımsız
sözleşmeler yazdığında, aynı sözleşmenin birden çok kez, birbirinden habersiz
tekrarlanması hiçbir yerde engellenmiyor. Sonuç yanlış değil — iki arayüz gerçekten
eşdeğer ve tip sistemi bunu doğru işliyor — ama kaynağı okuyan biri için **görünmez**
bir eşdeğerlik bu. Dört arayüzün her biri kendi tüketicisine göre en dar sözleşmeyi
kurduğu için yararlı olan aynı özgürlük, iki tüketici aynı ihtiyacı ayrı ayrı
tanımladığında fazladan bir sözleşmeye dönüşüyor.

## Özet

- Tüketici tarafında tanımlamada, üreten taraf hiçbir arayüz yazmadan bir tip üretiyor
  ve tüketen taraf kendi arayüzünü kendi ihtiyacına göre tanımlıyor.
- `Depo` dört tüketici arayüzünün dördünü de karşılıyor, kaynağında bu dört adın
  hiçbirine atıf yok; beşinci bir aday (`Yedekleyici`) karşılanmayarak sınırı gösteriyor.
- Küçük, tek yöntemli tüketici arayüzleri bu deseni pratikte kolaylaştırıyor — yöntem
  sayısı düştükçe karşılanma olasılığı artıyor.
- Go'nun arayüz karşılaştırması ada değil yöntem kümesine bakıyor; bu yüzden aynı imzayı
  taşıyan iki ayrı adlı arayüz, kaynakta hiçbir bağlantı yazılı olmasa da birbirinin
  yerine geçiyor.
- Bu, tüketici tarafında tanımlamanın bedelidir: bağımsız tüketiciler aynı sözleşmeyi
  habersizce yineleyebiliyor, ve bu yineleme yalnızca imzalar karşılaştırıldığında görünür
  oluyor.
- Eşdeğerliğin sınırı kesin: yöntem adı aynı olsa bile parametre ya da dönüş tipi
  farklıysa, iki arayüz birbirinin yerine geçmiyor; derleyici bu farkı doğrudan
  reddediyor.

## Sonraki Adım

Arayüzü tüketicinin tanımlaması, bu kurs boyunca ölçülen bir gerçeğin doğal sonucu: bağ
kaynakta hiç yazılmıyorsa, sözleşmeyi kimin yazdığı da üretici tarafın kararına
bağlanmıyor. Beş ders boyunca ölçülen çerçeve (bildirim, kullanım yeri, değer) burada da
geçerliydi: gömme bildirimde, tüketici tarafında tanımlama kullanım yerinde, ikisi de
aynı örtük gerçekleştirim ilkesinin iki farklı yüzü. Sıradaki konu bu modelin en çok
kullanıldığı yere bakıyor. Go'da hata bir **arayüzdür**, ve her hata değeri onu örtük
karşılar — üç yöntemli, dört yöntemli bir sözleşme değil, tek bir yöntemli en dar
sözleşme. Bir sonraki dersin sorusu: bir hata değerinin kimliği nerede duruyor, ve bir
hatayı hata yapan şey nedir?
