---
title: 'Sahteleme ve Arayüz Ayrımı'
source: 'https://academia.sh/tr/kurslar/go-uretim/sahteleme-ve-arayuz-ayrimi'
course: 'Üretim Go: Test, Araçlar ve Başarım'
language: tr
updated: '2026-09-13T05:08:46+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - "Bildirici arayüzünün StokAzalt'ı somut bir gerçekleştirmeye değil bir sözleşmeye bağladığını kaynaktan okur"
    - 'Aynı test gövdesini gerçek ve sahte bir gerçekleştirmeyle koşturup casus nesnenin çağrı sayısını okur'
    - 'Gerçek gerçekleştirmede tutan bir varsayımın sahte gerçekleştirmede hiç sınanmadığını, stoktan fazla azaltma durumuyla sınırlar'
---

# Sahteleme ve Arayüz Ayrımı

Aynı StokAzalt gövdesi hem kayıtlı hem casus bir Bildirici gerçekleştirmesiyle koşturuluyor: normal bir azaltmada ikisi de aynı kararı veriyor ve casus nesnenin çağrı sayısı 1 okunuyor. Sınırlayıcı ölçüm, stoktan fazlasını azaltan bir çağrıda gerçek gerçekleştirmenin negatif kalanı reddettiğini ama casus nesnenin aynı çağrıyı sessizce kaydettiğini gösteriyor.

Önceki ders bir HTTP işleyicisinin gerçek bir bağlantı hiç kurulmadan sınanabildiğini
ölçtü, ama işleyicinin kendisi hâlâ elle kurulan sabit bir depoya bakıyordu. Bu ders o
depoyu, işleyicinin arkasındaki bir başka bağımlılığa — bir bildirim gönderen taraf —
taşıyor ve aynı soruyu soruyor: bu bağımlılık gerçek dünyada yavaş, kararsız ya da
yan etkili bir şeyse (bir dış sisteme yazmak gibi), bu bağımlılığa dokunmadan aynı
mantığı nasıl sınarsınız?

Önceki derste bu soru bir bakıma zaten çözülmüştü — `httptest.NewRecorder`, standart
kitaplığın kendi `http.ResponseWriter` arayüzünü gerçekleştiren, önceden yazılmış bir
sahteydi. Bu ders aynı deseni, standart kitaplığın hazır bir sahtesi olmayan bir
bağımlılığa uyguluyor: `Bildirici`'yi ve onun iki gerçekleştirmesini bu paketin kendisi
yazıyor.

Sahte bağımlılık ailesi — sahte nesne, güdük, taklit nesne, casus nesne, kukla nesne — Birim Testi
ve Test Odaklı Geliştirme kursunda taksonomisiyle birlikte kuruldu ve burada yeniden tanıtılmıyor;
ne taksonomi yeniden anlatılıyor ne de her üye bu derste ayrı ayrı gerçekleştiriliyor. Bu dersin
eklediği soru dar: sahtelemeyi mümkün kılan şey nedir ve bu şey kaynakta nerede görünüyor?

Üretim kod tabanlarında bir işlevin çağırdığı her şeyi gerçek hâliyle sınamak çoğu zaman
mümkün değil: bir bildirim gönderen taraf gerçek bir dış sisteme yazabilir, bir ödeme
sağlayıcısı gerçek parayı hareket ettirebilir, bir depolama katmanı gerçek bir sunucuya
bağlanabilir. Bu bağımlılıkların yerine geçen bir sahte, sınamayı bu maliyetlerden
kurtarıyor — ama bu kurtuluşun kendi bedeli var ve bu dersin sınırlayıcı ölçümü tam
olarak bu bedeli sayıyor. Bedel görünmez kaldığı sürece bir risk taşımıyor; bu dersin
amacı bedeli görünür kılmak, sahtelemeyi terk ettirmek değil.

## Sahteyi Mümkün Kılan Şey Arayüz Ayrımı

`envanter` paketine bir işlev daha ekleniyor: `StokAzalt`, bir kalemin adedini azaltıyor
ve kalan miktar belirli bir eşiğin altına düştüğünde bir bildirim gönderiyor — eşik bu
derste sabit olarak 2 seçildi. Bildirimi gönderen tarafı `StokAzalt` doğrudan çağırmıyor
— bir `Bildirici` **arayüzü** üzerinden çağırıyor.

```go
// envanter.go — StokAzalt somut bir bildirim gonderen tarafa degil bir arayuze bagli
package main

import (
	"errors"
	"fmt"
)

type Kalem struct {
	Ad    string
	Adet  int
	Fiyat int
}

type Bildirici interface {
	Bildir(ad string, kalanAdet int) error
}

func StokAzalt(depo map[string]Kalem, ad string, miktar int, b Bildirici) error {
	k := depo[ad]
	k.Adet -= miktar
	depo[ad] = k
	if k.Adet < 2 {
		return b.Bildir(ad, k.Adet)
	}
	return nil
}

// kayitliBildirici gercek gerceklestirim: negatif kalani reddediyor.
type kayitliBildirici struct {
	kayitlar []string
}

func (k *kayitliBildirici) Bildir(ad string, kalanAdet int) error {
	if kalanAdet < 0 {
		return errors.New("negatif kalan bildirilemez")
	}
	k.kayitlar = append(k.kayitlar, fmt.Sprintf("%s:%d", ad, kalanAdet))
	return nil
}

// casusBildirici sahte gerceklestirim: dogrulama yapmadan cagriyi kaydediyor.
type casusBildirici struct {
	cagrilar int
}

func (c *casusBildirici) Bildir(ad string, kalanAdet int) error {
	c.cagrilar++
	return nil
}
```

**TS10.** `StokAzalt`'ın son parametresi `Bildirici` — bir yöntem imzasından ibaret bir
arayüz, `kayitliBildirici` ya da `casusBildirici` gibi somut bir tip değil. Bu, kaynaktan
doğrudan okunabilen bir karar: `StokAzalt`'ı çağıran kod, `Bildir` yöntemini taşıyan
**herhangi bir** tipi geçirebiliyor ve `StokAzalt`'ın gövdesi hangi somut tipin
geçirildiğini hiç bilmiyor. Sahtelemeyi mümkün kılan tam olarak bu: eğer `StokAzalt`
`*kayitliBildirici`'yi doğrudan alsaydı, onun yerine başka bir tip geçirmenin hiçbir
yolu olmazdı.

`envanter` paketi bu ders boyunca tek bir yeni bağımlılık kazandı ve bu bağımlılık
paketin kendi içinde tanımlandı — `Bildirici` gerçek bir dış paketten içe aktarılan bir
tip değil, `StokAzalt`'ın ihtiyaç duyduğu tek yöntemi tanımlayan, `envanter` paketinin
kendi arayüzü. Bu, arayüzü tüketen tarafın (burada `StokAzalt`) kendi ihtiyacına göre
küçük bir arayüz tanımlamasının bir örneği: arayüz, bir kitaplığın dışarıya sunduğu geniş
bir sözleşme değil, çağıranın gerçekten kullandığı kadarını kapsayan dar bir sözleşme.

`Bildirici` arayüzü tek bir yöntem taşıyor ve bu darlık kasıtlı: arayüz ne kadar dar
olursa, onu gerçekleştiren bir tip yazmak o kadar az iş gerektiriyor. `kayitliBildirici`
ve `casusBildirici` ikisi de yalnız bu tek yöntemi (`Bildir`) tanımlayarak arayüzü
sağlıyor — Go'da bir tipin bir arayüzü sağladığını ayrıca bildirmek gerekmiyor, yöntem
kümesi arayüzün istediği kümeyi kapsıyorsa tip o arayüzü kendiliğinden sağlamış oluyor.
Bu, `casusBildirici`'nin `envanter` paketinin geri kalanıyla hiçbir bağı olmadan,
yalnız `Bildir` yöntemini taşıdığı için `StokAzalt`'a geçirilebilmesinin nedeni.

Arayüzün kendisi, gerçek ve sahte gerçekleştirmenin **aynı sözleşmeyi** taşıdığını
garanti ediyor: ikisi de aynı imzayla (`Bildir(ad string, kalanAdet int) error`)
çağrılabiliyor, `StokAzalt`'ın gövdesi hangisiyle çalıştığını ayırt etmeden aynı satırı
(`b.Bildir(ad, k.Adet)`) çalıştırıyor. Ama bu sözleşme yalnız **imzayı** garanti ediyor —
imzanın arkasındaki **davranışı** değil. İki gerçekleştirmenin aynı girdiye aynı yanıtı
verip vermeyeceği, arayüzün kendisinden hiç okunamıyor; bu sorunun yanıtı aşağıdaki
ölçümde ortaya çıkıyor.

## Aynı Gövde, İki Gerçekleştirme

`StokAzalt`'ın gövdesinde `Bildirici` tipiyle ilgili tek bir satır var: `b.Bildir(ad, k.Adet)`. Bu
satırın hangi somut tiple çalıştığı, çağıranın `StokAzalt`'a hangi değeri geçirdiğine bağlı —
işlevin kendisi bu kararı hiç vermiyor. Aşağıdaki ölçüm bu kararı çağıran tarafına bırakarak, aynı
işlevi iki kez, iki farklı üçüncü argümanla çağırıyor.

**TS11.** Aynı `StokAzalt` çağrısı, önce gerçek gerçekleştirme (`kayitliBildirici`),
sonra sahte gerçekleştirme (`casusBildirici`) ile koşturuluyor; üç adetlik bir kalemden
iki adet azaltılıyor, kalan (1) eşiğin altında olduğu için bildirim tetikleniyor.

```go
// olcum.go — ayni govde gercek ve sahte gerceklestirmeyle kosuyor
package main

import "fmt"

func main() {
	fmt.Println("-- kosum: ayni govde gercek ve sahte ile --")
	depo1 := map[string]Kalem{"kalem": {"kalem", 3, 250}}
	gercek := &kayitliBildirici{}
	hataGercek := StokAzalt(depo1, "kalem", 2, gercek)
	fmt.Println("gercek: hata var mi:", hataGercek != nil, "kayit sayisi:", len(gercek.kayitlar))

	depo2 := map[string]Kalem{"kalem": {"kalem", 3, 250}}
	sahte := &casusBildirici{}
	hataSahte := StokAzalt(depo2, "kalem", 2, sahte)
	fmt.Println("sahte: hata var mi:", hataSahte != nil, "casus cagri sayisi:", sahte.cagrilar)
}
```

```
-- kosum: ayni govde gercek ve sahte ile --
gercek: hata var mi: false kayit sayisi: 1
sahte: hata var mi: false casus cagri sayisi: 1
```

Ölçüm iki ayrı `depo` değişkeni kuruyor (`depo1`, `depo2`), aynı içerikle ama ayrı
haritalar olarak — `StokAzalt` deponun kendisini değiştirdiği için, tek bir depo iki kez
kullanılsaydı ikinci çağrı ilk çağrının bıraktığı değişmiş durumdan başlardı ve iki
gerçekleştirmenin kararı artık aynı girdiye dayanmazdı. Bu yalıtım, önceki derslerdeki
`araclar.Kur`'un her koşum için tamamen boş bir modül kurmasıyla aynı gerekçeye
dayanıyor: bir ölçümün önceki bir ölçümün izini taşımaması.

İki koşumda da `StokAzalt`'ın gövdesi bayt bayt aynı; değişen tek şey son argümana
geçirilen değer. Gerçek gerçekleştirmede kayıt listesinin uzunluğu 1'e çıkıyor — bu
gerçek bir yan etkinin izini tutuyor. Sahte gerçekleştirmede aynı çağrı `cagrilar`
sayacını 1'e çıkarıyor — casus nesnenin ölçtüğü tek şey bu: **kaç kez** çağrıldığı, **ne**
yaptığı değil. İki ölçüm de aynı sonuca (`hata var mi: false`) varıyor ve bu ölçümün
bu kadarıyla sahtenin gerçeğin yerini tuttuğu söylenebilir.

`casusBildirici` burada özellikle casus nesne — sahte bağımlılık ailesinin, çağrıları
kaydedip sonradan sorgulanmasına izin veren üyesi. `Bildir` çağrıldığında hiçbir iş
yapmıyor, yalnız bir sayacı artırıyor; bu, `StokAzalt`'ın bildirim tetikleyip
tetiklemediğini sınamak için yeterli, çünkü ölçümün sorusu "bildirim gönderildi mi",
"bildirim doğru içerikte mi" değil. Aynı senaryo, çağrı sayısı yerine çağrının
argümanlarını da tutan bir casus ile genişletilebilirdi — `casusBildirici` bu derste
en dar hâliyle, yalnız bir sayaçla, tutuluyor çünkü ölçümün sorduğu tek şey bu.

`kayitliBildirici` adının kendisi de bir seçim: bu tip yalnızca çağrıları kaydeden bir
kukla değil, gerçekten karar veren bir gerçekleştirme — negatif bir kalan miktarı
reddetme kararı, sahte olmayan, üretim koduna gidecek bir mantık. Bu ders onu "gerçek"
diye anıyor çünkü `StokAzalt`'ın üretimde birlikte çalışacağı asıl gerçekleştirmenin
küçük bir modelini kuruyor — kendisi de bu derste hiçbir dış sisteme yazmıyor, ama
taşıdığı denetim kuralı gerçek bir dış sistemin taşıyabileceği türden.

## Sınırlayıcı Ölçüm: Sahte Gerçekleştirme Daha Az Şey Sınıyor

`StokAzalt`'ın kendisi, adedi negatife düşürecek bir çağrıyı reddetmiyor — bu bir
tasarım kararı: stoktan fazla azaltmanın nasıl ele alınacağı `StokAzalt`'ın değil,
`Bildirici`'nin sorumluluğuna bırakılmış. Bu, önceki ölçümde görünmeyen bir dal;
üç adetlik bir kalemden iki adet azaltıldığında kalan hep pozitif kalıyordu.

**TS12.** Aynı ölçüm bu kez üç adetlik bir kalemden beş adet azaltarak koşturuluyor —
kalan adet negatife düşüyor (-2) ve `Bildir` bu negatif değerle çağrılıyor.

```go
// sinir.go — gercek gerceklestirmede tutan bir varsayim sahtede hic sinanmiyor
package main

import "fmt"

func main() {
	fmt.Println("-- sinirlayici olcum: stoktan fazla azaltma --")
	depo3 := map[string]Kalem{"kalem": {"kalem", 3, 250}}
	gercek2 := &kayitliBildirici{}
	hataGercek2 := StokAzalt(depo3, "kalem", 5, gercek2)
	fmt.Println("gercek: hata var mi:", hataGercek2 != nil)

	depo4 := map[string]Kalem{"kalem": {"kalem", 3, 250}}
	sahte2 := &casusBildirici{}
	hataSahte2 := StokAzalt(depo4, "kalem", 5, sahte2)
	fmt.Println("sahte: hata var mi:", hataSahte2 != nil, "casus cagri sayisi:", sahte2.cagrilar)
}
```

```
-- sinirlayici olcum: stoktan fazla azaltma --
gercek: hata var mi: true
sahte: hata var mi: false casus cagri sayisi: 1
```

Çağrı sayısı iki gerçekleştirmede de aynı çıkıyor — `sahte2.cagrilar` yine 1, çünkü
`Bildir` yine tam olarak bir kez çağrılıyor. Değişen şey çağrının **sonucu**, çağrının
kendisi değil; casus nesne çağrının olup olmadığını doğru sayıyor, ama çağrının
taşıdığı değerin geçerli olup olmadığına hiç bakmıyor. Bu kez iki gerçekleştirme
**farklı** karar veriyor. `kayitliBildirici`, gerçek dünyada
bir dış sistemin taşıyabileceği bir varsayımı kodluyor: negatif bir kalan miktar
bildirilemez, çünkü böyle bir durum dış sistemin gözünde anlamsız. Bu varsayım ihlal
edildiğinde gerçek gerçekleştirme bir hata döndürüyor ve `StokAzalt`'ın kendisi de bu
hatayı olduğu gibi çağırana taşıyor. `casusBildirici` ise hiçbir varsayım taşımıyor —
yalnız çağrıldığını kaydediyor ve her zaman başarı bildiriyor; aynı negatif değerle
çağrıldığında sessizce `cagrilar`ı 1 artırıp `nil` döndürüyor.

Bu, konunun ölçü ekseninin bu derse özgü sınırı: yalnız `casusBildirici` kullanan bir
test takımı, `StokAzalt`'ın stoktan fazla azaltma durumunu doğru işlediğini hiçbir zaman
sınamaz — çünkü casus nesnenin kendisi bu durumu ayırt etmiyor, her çağrıyı aynı biçimde
kabul ediyor. Gerçek gerçekleştirmede tutan varsayım, sahte gerçekleştirmede hiç
sınanmadan kalıyor; sahteleme testi bu yüzden gerçek testten **daha az** şey sınıyor,
daha fazla değil. Bu bir kusur değil, sahtenin doğası: bir sahte, arkasındaki gerçek
sistemin bütün kurallarını değil, yalnız yazan kişinin kodladığı kadarını taşıyor.

Bunun üretim tarafındaki karşılığı somut: yalnız casus nesneyle yazılmış bir test takımı
yeşil görünse bile, gerçek dış sistem devreye girdiğinde `StokAzalt` bu senaryoda bir
hata ile karşılaşacak — ve bu hatayı üreten kural, dış sistemin kendi belgelerinde
yazılı olabilir ama sahte gerçekleştirmeye hiç taşınmamış olabilir. Bu, Test Paketi
Temelleri'nde ölçülen sınırın bir başka biçimi: orada geçen bir test doğru olduğunu
değil sınadığı şeyin tuttuğunu söylüyordu, burada geçen bir sahteleme testi de yalnız
sahtenin **kendi** kurallarının tuttuğunu söylüyor — gerçek sistemin kurallarının değil.
İki sınır da aynı köke iniyor: bir testin geçmesi, sınamanın kapsamı dışında kalan hiçbir
şey hakkında bir iddia taşımıyor. Sahtenin
gerçekleştirdiği yöntem sayısı ile gerçek gerçekleştirmenin yöntem sayısı burada aynı
(ikisi de yalnız `Bildir`), ama **davranış** sayısı aynı değil: gerçek gerçekleştirme
iki davranış taşıyor (kabul etme, reddetme), sahte gerçekleştirme yalnız birini (kabul
etme). Arayüzün kendisi bu farkı hiç göstermiyor, çünkü arayüz yalnız imzayı
zorunlu kılıyor, davranış sayısını değil.

Bu sınırın bir çıkış yolu var, ama bu dersin kapsamı dışında: sahte gerçekleştirmeyi
gerçek gerçekleştirmenin varsayımlarına göre periyodik olarak sınayan ayrı bir sözleşme
testi yazmak, iki tarafın davranışının birbirinden sapmadığını ayrıca doğrulayabilirdi.
Bu derste böyle bir sözleşme testi yok — amaç sahtenin sınırını göstermek, o sınırı
kapatan bir çözüm sunmak değil.

`casusBildirici` bu ölçüm için doğru seçim, çünkü sorulan soru "kaç kez çağrıldı" — sahte
bağımlılık ailesinin öbür üyeleri farklı sorulara yanıt vermek için var. Bir güdük sabit bir değer
döndürüp hiçbir şeyi kaydetmezdi; bir kukla nesne yalnız parametre yerini doldurur, `Bildir` hiç
çağrılmasa bile fark etmezdi. `StokAzalt`'ın burada ölçülen davranışı — bildirimin **tetiklenip
tetiklenmediği** ve **kaç kez** tetiklendiği — tam olarak bir casus nesnenin ölçtüğü tür bir
bilgi; bu yüzden beş üyeden bu ders yalnız onu kullandı.

## Özet

- `StokAzalt`'ın son parametresi somut bir tip değil bir arayüz (`Bildirici`); bu ayrım
  kaynaktan doğrudan okunabiliyor ve sahtelemeyi mümkün kılan tam olarak bu — sahteyi
  mümkün kılan bilgiyi **kaynak** söylüyor.
- Aynı `StokAzalt` gövdesi gerçek (`kayitliBildirici`) ve sahte (`casusBildirici`)
  gerçekleştirmeyle koşturuldu; normal bir azaltmada ikisi de aynı kararı verdi.
- Casus nesnenin ölçtüğü şey yalnız çağrı sayısı; ne yaptığı değil, kaç kez çağrıldığı.
- Sınırlayıcı ölçüm: stoktan fazla azaltan bir çağrıda gerçek gerçekleştirme negatif
  kalanı reddetti, sahte gerçekleştirme aynı çağrıyı sessizce kabul etti — sahteleme
  testi bu varsayımı hiç sınamadı.
- Sahte bağımlılık taksonomisi Birim Testi ve Test Odaklı Geliştirme kursunda kuruldu;
  burada yeniden anlatılmadı, yalnız Go'nun arayüz düzeneği üzerinden ölçüldü.

## Sonraki Adım

Bu derste bir arayüzün, bir bağımlılığı gerçek ve sahte gerçekleştirmeler arasında
değiştirilebilir kıldığı, ama sahte gerçekleştirmenin gerçeğin bütün varsayımlarını
taşımadığı ölçüldü. `Bildirici` tek yöntemli, dar bir arayüz olarak kaldı; `kayitliBildirici`
kendi denetim kuralını taşıdı, `casusBildirici` yalnız bir çağrı sayacı tuttu — ve iki
gerçekleştirme normal bir azaltmada aynı, aşırı bir azaltmada farklı karar verdi. Buraya
kadarki dört ders de tek tek geçti ya da kaldı, tek tek hata verdi ya da vermedi — ama
hiçbiri, bu kararların hangi **sayısının** koşumdan koşuma değiştiğini sormadı. Sıradaki
ders bu soruyu soruyor: aynı işin iki yazımı karşılaştırıldığında, hangi sayı beş ayrı
koşumda hep aynı kalıyor, hangisi kalmıyor — ve konunun bağlayıcı sayıları da orada
kuruluyor.
