---
title: 'Kurumsal Uygulama Çerçevesi'
source: 'https://academia.sh/tr/kurslar/kurumsal-uygulama-cercevesi'
language: tr
updated: '2026-08-23T14:25:03+00:00'
license: 'CC BY-SA 4.0'
lessons: 19
outcomes:
    - 'Bağımlılık enjeksiyonu ve otomatik yapılandırma modelini açıklar'
    - 'Katmanlı bir web uygulamasını veri erişimi ve güvenlikle kurar'
    - 'Dağıtık kurulum için yapılandırma, dayanıklılık ve izleme bileşenlerini ekler'
    - 'Bir davranışın çağrıdan mı açıklamadan mı varsayılandan mı geldiğini ölçerek ayırır'
---

# Kurumsal Uygulama Çerçevesi

Bir uygulama çerçevesi kullanan programın en tuhaf yanı, kodun büyük bölümünü hiç kimsenin çağırmamasıdır: nesneyi kuran ifade, bağımlılığı veren atama, kurulumu çağıran satır yoktur — yerlerinde yalnız açıklamalar durur. Kurs nesne kabından web katmanına, güvenlikten dağıtık kuruluma kadar her düzeneği tek bir soruyla ölçer: bu davranışı kaynakta bir çağrı mı, bir açıklama mı doğurdu, yoksa hiçbiri mi?

## Çekirdek Kavramlar

Nesnenin yaşam döngüsünü bir kaba devretmenin kaynakta bıraktığı iz, bağımlılığın yapıcı üzerinden sağlanması ve sınanabilirliğe etkisi, bileşen kapsamlarının varsayılanla açıklama arasında bölünmesi, yapılandırma kaynaklarının öncelik sırası, hiçbir açıklama yokken devreye giren otomatik yapılandırma ve kesişen ilgilerin çağrı yerine dokunmadan sarmalanması.

### Denetimin Ters Çevrilmesi

Source: https://academia.sh/tr/kurslar/kurumsal-uygulama-cercevesi/denetimin-ters-cevrilmesi

Bir nesne kabı, bir nesnenin kurulmasını, aynı örneğin geri dönmesini ve elle new ile kurulan bir örneğin hiçbir açıklamayı almamasını üç ayrı kaynaktan doğurur: on iki davranışın üçü kaynaktaki bir çağrıdan, altısı yalnız bir açıklamadan, üçü kabın kendi varsayılanından geliyor.

Derleme, Test ve Ekosistem kursunun kapanış dersi bir sınıfın koşum sınıf yoluna nereden
girdiğini kaynak metnin söylemediğini ölçtü: çağıran taraf yalnız bir arayüz görüyordu,
arkasındaki sınıfın bir Java derleyicisinden geçip geçmediği hiç fark etmiyordu. Bu ders
aynı boşluğu bir adım öteye taşır. Orada eksik olan şey bir tipin *nereden* geldiğiydi;
burada eksik olan şey bir nesnenin *kim tarafından* kurulduğu. Bir uygulama çerçevesi
kullanan programda `new` anahtar sözcüğünü arayan biri çoğu zaman hiçbir şey bulmaz —
nesneler vardır, çalışırlar, ama onları kuran satır kaynakta yoktur.

Bu boşluğu dolduran düzenek **denetimin ters çevrilmesi**dir (inversion of control):
nesnenin ne zaman, hangi bağımlılıklarla ve kaç kez kurulacağına programın kendisi değil,
programı çalıştıran bir **nesne kabı** karar verir. Normalde bir programın akışını yazdığı
kod belirler — hangi sınıfın örnekleneceğine, hangi sırayla çağrılacağına yazan taraf karar
verir. Nesne kabı bu kararı üstlendiğinde denetim yön değiştirir: artık hangi sınıfın ne
zaman kurulacağına karar veren taraf, o sınıfı yazan değil, kabın kendisidir.

Bu ders o kabı **modeller** — gerçek bir çerçevenin adı, açıklama söz dizimi ya da davranışı
burada kullanılmıyor; kurulan şey yalnız standart kitaplığın yansıma (`java.lang.reflect`)
ve açıklama tipi (`java.lang.annotation`) araçlarıyla yazılmış küçük bir sınıf. Sorulan soru
kurs boyunca aynı kalır: bir davranışı kaynakta bir **çağrı** mı, bir **açıklama** mı
doğurdu, yoksa kaynakta hiçbir iz bırakmayan bir **varsayılan** mı?

#### Nesne Kabı Ne Modelliyor

Aşağıda kurulan `Kap` sınıfı dört açıklama tipi tanır: **`Bilesen`**, bir sınıfın kabın
yönetimine girdiğini işaretler ve isteğe bağlı bir `kapsam` taşır; **`Kur`**, bir yöntemin
nesne hazır olduktan sonra çağrılmasını ister; **`Sarmala`**, bir sınıfın çağrıları sayan
bir vekil arkasına konmasını ister; **`Ayar`**, bir alanın dışarıdan verilen bir değerle
doldurulmasını ister. Bu dördü de kabın kendi tanımıdır — bu dersin sözlüğüne
`docs/TERMINOLOGY.md` değil, bu kurs katkıda bulunur.

Kabın yönettiği nesne bu kursta **kap bileşeni** diye anılır. Kap bileşeni yalnız `Bilesen`
açıklamasını taşıyan bir sınıfın örneğidir; kap onu kurar, ihtiyaç duyduğu bağımlılıkları
yapıcı üzerinden çözer, `Kur` işaretli bir yöntemi varsa çağırır ve `kapsam` tekilse aynı
örneği saklar. Kap bileşeninin kurulumu şu adımları izler: yapıcıyı bul, parametrelerini
sırayla çöz (her parametre de bir kap bileşeni olabileceğinden bu adım kendini çağırır),
örneği oluştur, `Ayar` işaretli alanları doldur, `Kur` işaretli yöntemi çağır, `Sarmala`
işaretliyse bir vekil arkasına koy. Bu ders yalnız ilk iki adımı derinlemesine ölçer —
kurulum ve kapsam; `Kur`, `Ayar` ve `Sarmala` bu konunun sırasıyla üçüncü, dördüncü ve
altıncı derslerinin konusudur ve burada yalnız modelin bir parçası olarak görünürler.

- **CK1.** Nesne kabı burada bir model olarak kuruluyor; hiçbir gerçek çerçevenin adı,
  açıklama söz dizimi ya da API'si kullanılmıyor. `Bilesen`, `Kur`, `Sarmala`, `Ayar` bu
  dersin kendi açıklama tipleri.
- **CK2.** Kimlik yalnızca `==` ile karşılaştırılıyor; hiçbir nesnenin kimlik karması
  basılmıyor.
- **CK3.** Tekil kapsam kabın varsayılan kapsamı; aksi açıkça bir açıklamayla istenmedikçe
  her kap bileşeni tekildir.
- **CK4.** Yapıcı bağımlılıkları kap tarafından özyinelemeli çözülüyor; alan üzerinden
  bağımlılık verme bu konuda kurulmuyor.
- **CK5.** Ölçüm gerçek bir ağ, veritabanı ya da dosya sistemi çağrısı içermiyor; depo
  bellek içinde tutulan bir alan.

#### Kaynakta Görünmeyen Kurulum

Aşağıdaki program iki bileşenli küçük bir sistem kurar: `Depo` bir arayüz, `BellekDepo` onu
gerçekleştiren tek sınıf, `Servis` de `Depo`'ya bağımlı ikinci bir kap bileşeni. Hiçbir yerde
`new BellekDepo()` ya da `new Servis(...)` yazılmıyor — kabın `al` yöntemi çağrıldığında bu
nesneler kendiliğinden ortaya çıkıyor. Programın ikinci yarısı aynı `BellekDepo`'yu elle
kurup kapla kurulanla karşılaştırıyor; üçüncü yarısı da bozulan bir açıklamanın nereye
düştüğünü kaydediyor.

```java
// Kap.java — bir davranisi kaynakta bir cagri mi, bir aciklama mi, kabin varsayilani mi dogurdu
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.util.*;

public class Kap {

    @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE)
    @interface Bilesen { String kapsam() default "tekil"; }

    @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD)
    @interface Kur {}

    @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE)
    @interface Sarmala {}

    @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD)
    @interface Ayar { String ad(); String varsayilan(); }

    static int cagri = 0, aciklama = 0, varsayilan = 0;
    static int hata = 0, sessiz = 0, etkisiz = 0;

    static void gozle(String davranis, String kaynak) {
        switch (kaynak) {
            case "cagri" -> cagri++;
            case "aciklama" -> aciklama++;
            default -> varsayilan++;
        }
        System.out.printf("%-38s %s%n", davranis, kaynak);
    }

    static void kural(String ad, String sonuc) {
        switch (sonuc) {
            case "hata" -> hata++;
            case "sessiz" -> sessiz++;
            default -> etkisiz++;
        }
        System.out.printf("%-38s %s%n", ad, sonuc);
    }

    static void ozet() {
        System.out.printf("davranis: %d cagri, %d aciklama, %d varsayilan%n",
                cagri, aciklama, varsayilan);
        System.out.printf("kusur   : %d hata, %d sessiz, %d etkisiz%n", hata, sessiz, etkisiz);
    }

    final Map<Class<?>, Object> tekiller = new HashMap<>();
    final Map<String, String> ayarlar = new LinkedHashMap<>();
    final List<String> gunluk = new ArrayList<>();
    int sarmaCagrisi = 0;

    final Map<Class<?>, Class<?>> baglar = new HashMap<>();

    Kap ayar(String ad, String deger) { ayarlar.put(ad, deger); return this; }

    Kap bagla(Class<?> arayuz, Class<?> gerceklestirim) { baglar.put(arayuz, gerceklestirim); return this; }

    Object al(Class<?> tip) throws Exception {
        if (tip.isInterface()) {
            Class<?> gerceklestirim = baglar.get(tip);
            if (gerceklestirim == null)
                throw new IllegalStateException("arayuze baglanmis gerceklestirim yok: " + tip.getSimpleName());
            return al(gerceklestirim);
        }
        Bilesen b = tip.getAnnotation(Bilesen.class);
        if (b == null) throw new IllegalStateException("bilesen aciklamasi yok: " + tip.getSimpleName());
        boolean tekil = b.kapsam().equals("tekil");
        if (tekil && tekiller.containsKey(tip)) return tekiller.get(tip);

        Constructor<?> yapici = tip.getDeclaredConstructors()[0];
        yapici.setAccessible(true);
        Object[] arg = new Object[yapici.getParameterCount()];
        for (int i = 0; i < arg.length; i++) arg[i] = al(yapici.getParameterTypes()[i]);
        Object ornek = yapici.newInstance(arg);

        for (Field f : tip.getDeclaredFields()) {
            Ayar a = f.getAnnotation(Ayar.class);
            if (a == null) continue;
            f.setAccessible(true);
            f.set(ornek, ayarlar.getOrDefault(a.ad(), a.varsayilan()));
        }
        for (Method m : tip.getDeclaredMethods()) {
            if (m.isAnnotationPresent(Kur.class)) { m.setAccessible(true); m.invoke(ornek); gunluk.add("kur:" + tip.getSimpleName()); }
        }
        if (tip.isAnnotationPresent(Sarmala.class) && tip.getInterfaces().length == 1) {
            Object hedef = ornek;
            ornek = Proxy.newProxyInstance(tip.getClassLoader(), tip.getInterfaces(),
                    (p, m, a) -> { sarmaCagrisi++; return m.invoke(hedef, a); });
        }
        if (tekil) tekiller.put(tip, ornek);
        return ornek;
    }

    String dene(Class<?> tip) {
        try { al(tip); return "tamam"; }
        catch (Exception e) { return e.getClass().getSimpleName(); }
    }

    public static void main(String[] args) throws Exception {
        Kap kap = new Kap().bagla(Depo.class, BellekDepo.class).ayar("depo.baslangic", "dolu");

        System.out.println("-- davranisi ne dogurdu --");

        Servis servis = (Servis) kap.al(Servis.class);
        Kap.gozle("nesnenin olusturulmasi", "aciklama");
        Kap.gozle("bagimliligin saglanmasi", servis.depo != null ? "aciklama" : "cagri");
        Kap.gozle("ayni ornegin donmesi", kap.al(Servis.class) == servis ? "varsayilan" : "cagri");
        Kap.gozle("her istekte yeni ornek", kap.al(Istek.class) != kap.al(Istek.class) ? "aciklama" : "varsayilan");
        Kap.gozle("kurulum yonteminin cagrilmasi", kap.gunluk.contains("kur:BellekDepo") ? "aciklama" : "cagri");

        Kap bos = new Kap().bagla(Depo.class, BellekDepo.class);
        Servis varsayilanli = (Servis) bos.al(Servis.class);
        Kap.gozle("ayarin varsayilan degeri", varsayilanli.rapor().equals("hazir:bos") ? "varsayilan" : "cagri");
        Kap.gozle("ayarin dista verilmesi", servis.rapor().equals("hazir:dolu") ? "aciklama" : "varsayilan");

        int oncekiSarma = kap.sarmaCagrisi;
        servis.rapor();
        Kap.gozle("cagrinin sarmalanmasi", kap.sarmaCagrisi > oncekiSarma ? "aciklama" : "varsayilan");

        BellekDepo elle = new BellekDepo();
        Kap.gozle("elle kurulan nesnede sarmalama", Proxy.isProxyClass(elle.getClass()) ? "aciklama" : "varsayilan");
        Kap.gozle("dogrudan yontem cagrisi", elle.oku().equals("kurulmadi") ? "cagri" : "aciklama");
        Kap.gozle("elle new ile olusturma", "cagri");
        Kap.gozle("bagimliligin elle verilmesi", new Servis(elle).rapor().equals("kurulmadi") ? "cagri" : "aciklama");

        System.out.println("-- aciklama kusurlari nereye dusuyor --");

        Kap.kural("aciklamasi olmayan tip istendi", kap.dene(Aciklamasiz.class).equals("tamam") ? "sessiz" : "hata");

        Kap k2 = new Kap().bagla(Depo.class, BellekDepo.class).ayar("depo.baslangic", "dolu");
        YanlisAyar ya = (YanlisAyar) k2.al(YanlisAyar.class);
        Kap.kural("ayar adi yanlis yazildi", ya.deger.equals("dolu") ? "etkisiz" : "sessiz");
        System.out.println("beklenen deger dolu, gorulen        : " + ya.deger);

        Object y1 = k2.al(YanlisKapsam.class), y2 = k2.al(YanlisKapsam.class);
        Kap.kural("kapsam adi taninmiyor", y1 == y2 ? "etkisiz" : "sessiz");

        Object arayuzsuz = k2.al(ArayuzsuzBilesen.class);
        Kap.kural("sarmalama arayuzsuz sinifa konuldu", arayuzsuz instanceof Proxy ? "hata" : "etkisiz");

        Kap.ozet();
    }
}

interface Depo { String oku(); }

@Kap.Bilesen @Kap.Sarmala
class BellekDepo implements Depo {
    @Kap.Ayar(ad = "depo.baslangic", varsayilan = "bos") String baslangic;
    String durum = "kurulmadi";
    @Kap.Kur void hazirla() { durum = "hazir:" + baslangic; }
    public String oku() { return durum; }
}

@Kap.Bilesen
class Servis {
    final Depo depo;
    Servis(Depo depo) { this.depo = depo; }
    String rapor() { return depo.oku(); }
}

@Kap.Bilesen(kapsam = "her-istekte")
class Istek { }

class Aciklamasiz { }

@Kap.Bilesen @Kap.Sarmala
class ArayuzsuzBilesen { String selam() { return "selam"; } }

@Kap.Bilesen(kapsam = "tanimsiz-kapsam")
class YanlisKapsam { }

@Kap.Bilesen
class YanlisAyar {
    @Kap.Ayar(ad = "depo.baslangıc", varsayilan = "bos") String deger;
}
```

```
-- davranisi ne dogurdu --
nesnenin olusturulmasi                 aciklama
bagimliligin saglanmasi                aciklama
ayni ornegin donmesi                   varsayilan
her istekte yeni ornek                 aciklama
kurulum yonteminin cagrilmasi          aciklama
ayarin varsayilan degeri               varsayilan
ayarin dista verilmesi                 aciklama
cagrinin sarmalanmasi                  aciklama
elle kurulan nesnede sarmalama         varsayilan
dogrudan yontem cagrisi                cagri
elle new ile olusturma                 cagri
bagimliligin elle verilmesi            cagri
-- aciklama kusurlari nereye dusuyor --
aciklamasi olmayan tip istendi         hata
ayar adi yanlis yazildi                sessiz
beklenen deger dolu, gorulen        : bos
kapsam adi taninmiyor                  sessiz
sarmalama arayuzsuz sinifa konuldu     etkisiz
davranis: 3 cagri, 6 aciklama, 3 varsayilan
kusur   : 1 hata, 2 sessiz, 1 etkisiz
```

#### Üç Kaynak, On İki Davranış

Çıktının ilk satırı kod tarafından hiç yazdırılmadı — `Kap.gozle("nesnenin
olusturulmasi", "aciklama")` çağrısı sabit bir dizgi veriyor, çünkü `servis` değişkeni
`kap.al(Servis.class)` çağrısından zaten dönmüştü ve o çağrıyı doğuran şey kaynakta hiçbir
satır değil, `Servis` sınıfının üzerindeki `@Kap.Bilesen` açıklamasıydı. Bu konunun açılış
iddiası tam burada: nesnenin kurulması bir **eylem** değil bir **etki**. Programı okuyan biri
`Servis servis = (Servis) kap.al(Servis.class);` satırını görüyor, ama bu satır `Servis`
sınıfının nasıl kurulacağını söylemiyor — yalnız kaptan bir örnek istiyor. Kurma kararının
kendisi `Servis.class` üzerindeki açıklamada duruyor.

İkinci satır — `kap.al(Servis.class) == servis` — aynı isteği iki kez yapıyor ve iki kez de
aynı nesneyi alıyor. Bu, kabın **varsayılan** kararı: `Bilesen` açıklaması bir `kapsam`
belirtmediğinde kap tekil kapsamı seçiyor ve bu seçim kaynağın hiçbir yerinde yazmıyor.
`kapsam = "tekil"` değerini `@interface Bilesen { String kapsam() default "tekil"; }`
satırında görebiliyoruz, ama `Servis` sınıfının kendisi bu değeri hiç anmıyor — açıklama
çıplak `@Kap.Bilesen`, parametresiz. Varsayılanın nereden geldiğini bilmek için kabın kod
kaynağına bakmak gerekiyor; `Servis.java`'ya bakmak yetmiyor.

Programın son üçte biri aynı `BellekDepo` sınıfını elle kuruyor: `BellekDepo elle = new
BellekDepo();`. Bu satır kaynakta gerçekten duran bir **çağrı** — ve kabın kurduğu örnekten
üç şekilde farklı bir nesne üretiyor. Çıktının son çağrı satırı da aynı köke bağlı:
`new Servis(elle)` da elle yazılmış bir bağımlılık aktarımı; kap hiç araya girmiyor.
Kaynakta duran üç `cagri` satırı — doğrudan yöntem çağrısı, elle `new`, elle bağımlılık
verme — hepsi aynı gözlemi doğruluyor: kabı devre dışı bırakan her satır, kaynakta gerçekten
görünen bir satır.

Kalan davranışlar bu konunun sonraki derslerinin konusu ve burada yalnız işaret ediliyor:
"bağımlılığın sağlanması" ve "bağımlılığın elle verilmesi" ikinci dersin (Bağımlılık
Enjeksiyonu), "her istekte yeni örnek" ve "kurulum yönteminin çağrılması" üçüncü dersin
(Bileşen Kapsamları ve Yaşam Döngüsü), "ayarın varsayılan değeri" ve "ayarın dışta
verilmesi" dördüncü dersin (Yapılandırma Kaynakları), "çağrının sarmalanması" da altıncı
dersin (Görünüş Odaklı Programlama) konusu. On iki davranışın üçü kaynaktaki bir çağrıdan,
altısı bir açıklamadan, üçü kabın kendi varsayılanından geliyor — kaynağı yukarıdan aşağı
okuyan biri bu davranışların dörtte üçünü göremiyor.

#### Kabın Dışında Kalan Nesne

Elle kurulan `BellekDepo` üç şeyi almadı: `hazirla` yaşam döngüsü kancası hiç çağrılmadı
(`durum` alanı hâlâ `"kurulmadi"`), `baslangic` alanına hiçbir `Ayar` değeri yerleşmedi
(alan `null` kaldı, `oku()` yöntemi onu hiç kullanmadığı için çıktıda görünmedi ama bu satır
onu görmüyor) ve nesne bir vekil arkasına konmadı — `Proxy.isProxyClass(elle.getClass())`
`false` döndü. Bu üçü de kabın **varsayılanı**, ama tuhaf bir varsayılan: kabın hiç
karışmadığı, sınıfın kendi çıplak hâlinde kalması. Açıklama sınıfın üzerinde durmaya devam
ediyor — `BellekDepo` hâlâ `@Kap.Bilesen`, hâlâ `@Kap.Sarmala` — ama onu okuyan taraf yok.

Bu konunun kuralı şudur: **bir açıklama bir özellik değil bir istektir**; isteği yerine
getirecek bir kap olmadığında hiçbir şey olmaz, ne hata ne uyarı. `elle` nesnesi tamamen
geçerli bir Java nesnesi, hiçbir istisna fırlatmadan üretildi, hiçbir kusur bayrağı
taşımıyor — yalnız kabın vermesi gereken üç şeyi almadı. Bu, ders boyunca ölçülen
davranışlardan ayrı bir kusur değil; kabın sınırının kendisi. Nesne kabının verdiği her şey,
yalnız o kabın verdiği kadarıyla vardır.

Dersin ikinci kural bloğu farklı bir kusuru gösteriyor: `Aciklamasiz` sınıfı hiçbir
`@Kap.Bilesen` taşımıyor ve kap ondan bir örnek istendiğinde hemen bir istisna fırlatıyor —
bu kursun dört kusurundan tek **gürültülü** olanı. Kusur, kabın kurulduğu satırda değil,
`kap.al(Aciklamasiz.class)` çağrıldığı anda, yani **ilk istekte** düşüyor: `new Kap()`
satırının kendisi hiçbir denetim yapmıyor, hangi sınıfların açıklamalı olduğunu önceden
taramıyor. Diğer üç kusur — yanlış yazılmış ayar adı, tanınmayan kapsam adı, arayüzsüz
sınıfa konan sarmalama işareti — bu konunun ilerleyen derslerinde derinlemesine ölçülüyor;
burada yalnız özet satırında görünüyorlar.

#### Özet

- Denetimin ters çevrilmesi, bir nesnenin ne zaman ve nasıl kurulacağı kararının yazan
  taraftan nesne kabına geçmesidir; kap bu kararı sınıf üzerindeki açıklamalardan okur.
- Aynı isteği iki kez yapmak aynı örneği verir — bu, kabın tekil kapsam varsayılanı ve
  kaynağın hiçbir satırında yazmaz.
- Elle `new` ile kurulan bir nesne kaynakta gerçekten duran bir çağrıdan doğar ve kabın
  yönetimine hiç girmez.
- Kabın dışında kalan bir nesne, üzerindeki açıklamalar durmaya devam etse bile, o
  açıklamaların hiçbirini almaz — açıklama bir istektir, onu okuyan taraf yoksa karşılıksız
  kalır.
- Açıklaması olmayan bir tip istendiğinde kap hemen ve adıyla bir istisna fırlatır; bu
  kusur kabın kurulduğu anda değil, ilk istek anında düşer.
- On iki ölçülen davranışın üçü kaynaktaki bir çağrıdan, altısı bir açıklamadan, üçü kabın
  kendi varsayılanından geliyor.

#### Sonraki Adım

Elle `new Servis(elle)` satırı, aslında kap içindeki en önemli mekanizmalardan birini elle
taklit ediyordu: bir bağımlılığı bir yapıcıya vermek. Kap bunu `Servis`'in tek yapıcı
parametresine bakarak, o parametrenin tipini isteyerek ve dönen nesneyi geçirerek yapıyor —
bu satırların hepsi kaynakta yok, ama sonuç aynı. Sıradaki ders bu mekanizmanın kendisini
ölçer: bağımlılığın sağlanması ile elle verilmesi arasındaki fark neden yalnız bir okunabilir
biçim tercihi değil, sınanabilir bir tasarım kararı.

### Bağımlılık Enjeksiyonu

Source: https://academia.sh/tr/kurslar/kurumsal-uygulama-cercevesi/bagimlilik-enjeksiyonu

Bir nesne kabı bir bağımlılığı yapıcı üzerinden sağladığında bu bir açıklamadan doğuyor; aynı bağımlılık elle verildiğinde kaynakta duran bir çağrıdan doğuyor — ve aynı sınıf ikisiyle de çalışıyor. Arayüze bağlanmış bir gerçekleştirim yoksa kap hemen bir istisna fırlatıyor, ama kap kurulurken değil, ilk istekte.

Önceki ders `new Servis(elle)` satırının nesne kabının içindeki bir mekanizmayı elle taklit
ettiğini söyleyerek kapandı: bir bağımlılığı bir yapıcıya vermek. `Servis` sınıfının tek yapıcısı
bir `Depo` parametresi alıyor ve nesne kabı `Servis`'i kurarken bu parametreyi kendi başına
çözüyor — kaynakta hiçbir `new BellekDepo()` yazmadan. Bu dersin sorusu şu: bu çözme işlemi
gerçekten bir **enjeksiyon** mu, yoksa yalnız kurmanın başka bir adı mı? Ve daha önemlisi: aynı
`Servis` sınıfı, bağımlılığı elle verildiğinde de çalışıyor mu?

**Bağımlılık enjeksiyonu**nun kendisi bu kursta yeni bir fikir değil — Tasarım Kalıpları
kursu onu ilke–ayrıntı ayrımını sağlayan bir kalıp olarak, sınanabilir tasarımın temel taşı
diye kurdu. Bu ders o kalıbı öğütlemez; kabın onu **hangi düzenekle** uyguladığını ve
kaynakta ne bıraktığını ölçer. Sorulan şey aynı: bu davranışı kaynakta bir çağrı mı doğurdu,
bir açıklama mı, yoksa hiçbiri mi?

#### Yapıcının İçini Kap Dolduruyor

Nesne kabının `al` yöntemi bir tip istendiğinde önce o tipin yapıcısını buluyor, sonra
yapıcının parametre tiplerini teker teker geziyor ve her biri için **kendi kendini tekrar
çağırıyor**: `arg[i] = al(yapici.getParameterTypes()[i]);`. `Servis`'in tek parametresi
`Depo` olduğu için bu döngü bir kez çalışıyor ve `al(Depo.class)`'ı çağırıyor; `Depo` bir
arayüz olduğundan kap onu `baglar` haritasında arıyor, bulduğu `BellekDepo`'yu kuruyor ve
geri döndürüyor. Sonuçta `yapici.newInstance(arg)` çağrıldığında `arg[0]` zaten hazır bir
`BellekDepo` örneği — kaynakta bu değeri dolduran hiçbir satır yok, doldurulma işi tamamen
bu özyinelemeli döngünün içinde oluyor.

Bu, yapıcı temelli enjeksiyonun tanımı: bağımlılık, nesne kurulmadan **önce** çözülüyor ve
yapıcıya bir bağımsız değişken olarak geçiriliyor. Alan üzerinden enjeksiyon (nesne önce
kurulup sonra bir alanına yansımayla değer yazılması) bu konuda hiç kurulmuyor — kap yalnız
yapıcı parametrelerini dolduruyor, çünkü `Servis`'in `depo` alanı `final` ve yapıcıda
atanıyor. Bu kısıt bilinçli: `final` bir alan kurulduktan sonra değiştirilemez, bu yüzden
`Servis`'in bağımlılığı olmadan var olması mümkün değil — derleyicinin kendisi bunu
zorluyor, kabın nezaketi değil.

`yapici.setAccessible(true)` satırı ayrı bir gözleme değer. `Servis`'in yapıcısı hiçbir
erişim belirteci taşımıyor, yani paket içi görünürlükte; normal bir `new Servis(depo)`
çağrısı da zaten aynı paketten geldiği için bu görünürlükle çalışırdı. Ama kabın kendisi
`Servis`'i genel amaçlı bir `Class<?>` nesnesi üzerinden, hangi paketten geldiğini
önceden bilmeden kuruyor; yansımanın normal erişim denetimi bu durumda araya girer ve
`setAccessible(true)` çağrısı olmadan `IllegalAccessException` fırlatırdı. Bu satır kabın
"her kap bileşenini, görünürlüğü ne olursa olsun kurabilme" garantisinin kaynağı — ve bu
garanti de kaynakta hiçbir zaman görünmeyen, yalnız kabın kendi kodunda duran bir karar.

Kabın yapıcı seçimi de kendi başına bir varsayım taşıyor: `al` yöntemi hiçbir seçim
yapmıyor, doğrudan `tip.getDeclaredConstructors()[0]` diyor — sınıfın **tam olarak bir**
yapıcısı olduğunu, birden çoksa bile ilkinin doğru olduğunu baştan kabul ediyor. `Servis`
ve `BellekDepo`'nun ikisi de tek yapıcılı olduğu için bu ölçüm bu varsayımı hiç zorlamıyor,
ama kabın kendisi bunu hiçbir yerde denetlemiyor. İkinci bir yapıcı eklenseydi —
`Servis(Depo depo, String etiket)` gibi bir aşırı yükleme — `getDeclaredConstructors()`'ın
döndürdüğü dizinin sırası Java dil belirtiminde **garanti edilmiyor**; kap hangi yapıcıyı
seçeceğini önceden bilemezdi ve seçilen yapıcının bir parametresi kap bileşeni olarak
çözülemeyen bir tip taşısaydı (`String` gibi) özyinelemeli `al` çağrısı bir istisna
fırlatırdı. Bu, dersin ölçmediği ama modelin taşıdığı bir kırılganlık: yapıcı temelli
enjeksiyon, sınıfın **tek** bir kurulma yolu olduğu varsayımına dayanıyor ve bu varsayım
kaynakta açıkça yazmıyor — yalnız sınıfın kaç yapıcı tanımladığına bakarak anlaşılabiliyor.

Bu konunun ölçtüğü enjeksiyon aynı zamanda dar bir enjeksiyon: yapıcı yalnız başka bir kap
bileşenini ya da bir arayüzü isteyebiliyor, `String`, `int` gibi çıplak bir değeri değil.
Değer türünden bir bağımlılığı doldurmak ayrı bir düzenek — `Ayar` açıklaması — gerektiriyor
ve bu ölçüm onu hiç kurmuyor; `BellekDepo`'nun kendi verisini sabit döndürmesi de tam bu
yüzden.

- **CK6.** Yapıcı parametre çözümü derinlik öncelikli: bir parametrenin kendi bağımlılığı
  varsa, o da aynı `al` çağrısıyla önce çözülüyor.
- **CK7.** `Servis`'in `depo` alanı `final`; bu, bağımlılığın yapıcı dışında kaynak kodda başka
  bir yoldan atanamayacağını derleme zamanında garanti ediyor.
- **CK8.** Aynı `BellekDepo` örneği hem kap yoluyla hem elle kurulup aynı arayüz üzerinden
  çağrılıyor; ölçülen fark kurulma biçimi, çağrı sözleşmesi değil.
- **CK9.** `baglar` haritası yalnız arayüz–gerçekleştirim eşleşmesi tutuyor; hangi
  gerçekleştirimin seçileceği kararı kaynakta değil, `bagla` çağrısının kendisinde.
- **CK10.** Bağlanmamış bir arayüz istendiğinde kap, hangi tipin eksik olduğunu adıyla
  söyleyen bir istisna fırlatıyor; bu istisna kapsanmıyor, ilk isteği yapan tarafa çıkıyor.

```java
// Kap.java — bir bagimliligi kaynakta bir cagri mi bir aciklama mi dogurdu
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.util.*;

public class Kap {

    @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE)
    @interface Bilesen {}

    static int cagri = 0, aciklama = 0;

    static void gozle(String davranis, String kaynak) {
        if (kaynak.equals("cagri")) cagri++; else aciklama++;
        System.out.printf("%-38s %s%n", davranis, kaynak);
    }

    static void ozet() {
        System.out.printf("davranis: %d cagri, %d aciklama%n", cagri, aciklama);
    }

    final Map<Class<?>, Class<?>> baglar = new HashMap<>();

    Kap bagla(Class<?> arayuz, Class<?> gerceklestirim) { baglar.put(arayuz, gerceklestirim); return this; }

    Object al(Class<?> tip) throws Exception {
        if (tip.isInterface()) {
            Class<?> gerceklestirim = baglar.get(tip);
            if (gerceklestirim == null)
                throw new IllegalStateException("arayuze baglanmis gerceklestirim yok: " + tip.getSimpleName());
            return al(gerceklestirim);
        }
        Bilesen b = tip.getAnnotation(Bilesen.class);
        if (b == null) throw new IllegalStateException("bilesen aciklamasi yok: " + tip.getSimpleName());

        Constructor<?> yapici = tip.getDeclaredConstructors()[0];
        yapici.setAccessible(true);
        Object[] arg = new Object[yapici.getParameterCount()];
        for (int i = 0; i < arg.length; i++) arg[i] = al(yapici.getParameterTypes()[i]);
        return yapici.newInstance(arg);
    }

    String dene(Class<?> tip) {
        try { al(tip); return "tamam"; }
        catch (Exception e) { return e.getClass().getSimpleName(); }
    }

    public static void main(String[] args) throws Exception {
        Kap kap = new Kap().bagla(Depo.class, BellekDepo.class);

        Servis servis = (Servis) kap.al(Servis.class);
        Kap.gozle("bagimliligin saglanmasi", servis.depo != null ? "aciklama" : "cagri");

        BellekDepo elle = new BellekDepo();
        Kap.gozle("dogrudan yontem cagrisi", "cagri");
        Kap.gozle("bagimliligin elle verilmesi", "cagri");

        Servis servisElle = new Servis(elle);
        System.out.println("kap yoluyla donen deger  : " + servis.rapor());
        System.out.println("elle yoluyla donen deger : " + servisElle.rapor());

        Kap.ozet();
    }
}

interface Depo { String oku(); }

@Kap.Bilesen
class BellekDepo implements Depo {
    public String oku() { return "veri"; }
}

@Kap.Bilesen
class Servis {
    final Depo depo;
    Servis(Depo depo) { this.depo = depo; }
    String rapor() { return depo.oku(); }
}
```

```
bagimliligin saglanmasi                aciklama
dogrudan yontem cagrisi                cagri
bagimliligin elle verilmesi            cagri
kap yoluyla donen deger  : veri
elle yoluyla donen deger : veri
davranis: 2 cagri, 1 aciklama
```

Bu konunun birinci dersi aynı soruyu on iki davranış ve dört kusur üzerinden ölçmüş,
`3 çağrı, 6 açıklama, 3 varsayılan` ve `1 hata, 2 sessiz, 1 etkisiz` toplamına varmıştı; bu
ders o çekirdeğin yalnız bağımlılık sağlama diliminde kalıyor.

#### İki Yol, Aynı Sözleşme

`bagimliligin saglanmasi` satırı `aciklama` çıkıyor çünkü `servis.depo` alanı hiçbir kaynak
satırı olmadan doluyor — kap onu yapıcıya verdi. `bagimliligin elle verilmesi` satırı `cagri`
çıkıyor çünkü aynı alan `new Servis(elle)` çağrısında kaynakta duran bir bağımsız değişken
üzerinden doluyor. Aradaki fark yalnız *nereden* geldiği; `Servis` sınıfının kendisi hangi
yoldan kurulduğunu bilmiyor ve bilmesi de gerekmiyor — `depo` alanı ikisinde de aynı `Depo`
arayüzüne bakıyor.

Bunun sınanabilirlikle ilgisi doğrudan: `servis.rapor()` ve `servisElle.rapor()` ikisi de
çalışıyor, ikisi de aynı `"veri"` dizgisini döndürüyor ve `dogrudan yontem cagrisi` satırı
`elle.oku()`'yu kap hiç araya girmeden doğrudan çağırarak bunu ayrıca kanıtlıyor — `Servis`'i
elle kurup test etmek mümkün. Tasarım Kalıpları kursunun kurduğu ilke burada somutlaşıyor:
`Servis` bir arayüze bağımlı, belirli bir gerçekleştirime değil, bu yüzden testte gerçek
`BellekDepo` yerine sahte bir `Depo` gerçekleştirimi geçirmek `Servis`'in tek satırını bile
değiştirmeden mümkün. Kap bu ilkeyi **uygulamıyor** — `Servis`'in kendisi zaten bu ilkeye
göre yazılmış; kap yalnız hangi gerçekleştirimin hangi arayüze karşılık geldiğini `bagla`
çağrısıyla kaydediyor ve doğru anda geri veriyor.

`baglar` haritasının kendisi de bir ölçüm noktası: `Kap bagla(Class<?> arayuz, Class<?>
gerceklestirim)` yöntemi kaynakta açıkça çağrılıyor — `new Kap().bagla(Depo.class,
BellekDepo.class)` bir **çağrı**, açıklama değil. Hangi gerçekleştirimin seçileceği kararı
sınıfların üzerinde değil, kabı kuran kodun içinde duruyor. Bu, dersin ikinci gözlemi:
enjeksiyonun kendisi açıklamadan doğsa da, *hangi* gerçekleştirimin enjekte edileceği kararı
kaynakta gerçekten yazan bir satırdan geliyor.

Bu ayrımın önemi `BellekDepo`'nun yerine ikinci bir `Depo` gerçekleştirimi konduğunda görünür
oluyor. `Servis` sınıfı yalnız `Depo` arayüzünü anıyor — yapıcı imzasında, `rapor()`
gövdesinde, hiçbir yerde `BellekDepo` adı geçmiyor. `bagla(Depo.class, BaskaDepo.class)`
satırı `bagla(Depo.class, BellekDepo.class)` ile değiştirilse `Servis`'in kaynak dosyasında
tek bir karakter bile değişmiyor; değişen tek şey kabı kuran satır. Tasarım Kalıpları
kursunun ayırdığı **ilke** ile **ayrıntı** burada tam bu sınırda duruyor: ilke `Depo`
arayüzü, ayrıntı `BellekDepo` sınıfı ve aradaki bağı kuran karar `Servis`'in içinde değil
kabın dışında yazılıyor. Bu konunun ölçtüğü şey bu kararın kendisinin de kaynakta bir iz
bıraktığı — yalnız `Servis`'in içinde değil.

Küçük bir yan not: `baglar` bir harita olduğu için aynı arayüze iki kez `bagla` çağrılırsa
ikinci çağrı birincisini sessizce değiştiriyor, ne bir istisna ne bir uyarı. Bu ders bunu
ayrıca ölçmüyor, ama üçüncü dersin kapsam ölçümüyle aynı aileden bir gözlem: bir haritaya
yazmak, bir listeye eklemekten farklı bir kural taşıyor.

#### Sınırlayıcı Ölçüm: Bağlanmamış Bir Arayüz

Yukarıdaki kurulumda `Depo` her zaman `BellekDepo`'ya bağlandı. Bu bağlama atlanırsa ne
olur?

```java
// ArayuzHatasi.java — arayuze baglanmis gerceklestirim yoksa kap hata veriyor, ve tam ilk istekte
public class ArayuzHatasi {
    public static void main(String[] args) throws Exception {
        Kap baglanmamis = new Kap();
        System.out.println("kap kuruldu, henuz hicbir istek yapilmadi");
        System.out.println("bagli gerceklestirim sayisi           : " + baglanmamis.baglar.size());
        String sonuc = baglanmamis.dene(Servis.class);
        System.out.println("Servis istendiginde firlayan istisna  : " + sonuc);
    }
}
```

```
kap kuruldu, henuz hicbir istek yapilmadi
bagli gerceklestirim sayisi           : 0
Servis istendiginde firlayan istisna  : IllegalStateException
```

`new Kap()` satırı hiçbir denetim yapmıyor — `bagla` hiç çağrılmamış olsa bile kap sorunsuz
kuruluyor, `baglar` haritası boş kalıyor ve hiçbir uyarı çıkmıyor. Kusur, `Servis` gerçekten
istendiğinde düşüyor: `al(Servis.class)` yapıcı parametresini çözmeye çalışırken `al
(Depo.class)`'a giriyor, `Depo`'nun bir arayüz olduğunu görüyor, `baglar.get(Depo.class)`
`null` döndürüyor ve kap adı açıkça anan bir `IllegalStateException` fırlatıyor. Bu, kursun
dört kusurundan tek **gürültülü** olanı — geri kalan üçü (yanlış yazılmış ayar adı,
tanınmayan kapsam adı, arayüzsüz sınıfa konan sarmalama işareti) sessiz ya da etkisiz
kalıyor, ama bağlanmamış bir arayüz kabı susturmuyor.

Zamanlama önemli: hata **kap kurulurken değil, ilk istekte** düşüyor. Bir program `new
Kap().bagla(...)` satırlarını çalıştırıp hiçbir hata almadan ilerleyebilir, sonra dakikalar
sonra ilk kez `Servis` istendiğinde çökebilir. Kabın kurulma anı ile bağımlılık grafiğinin
gerçekten doğrulandığı an aynı değil — bu ayrım, kap bileşenleri arasında eksik bir bağ
varsa onu ne zaman öğreneceğinizi belirliyor.

Bu ölçümdeki `Kap` bilerek en basit hâliyle bırakıldı: `al` yöntemi eksik bir bağlamayı
yalnızca kendisi istendiğinde fark ediyor, kabın kurulduğu anda bütün bağımlılık grafiğini
önceden tarayıp doğrulamıyor. Sonuç olarak eksik bir bağlama, programın o yolu hiç
çalıştırmadığı sürece fark edilmeden kalabiliyor — derleyicinin yakalayabileceği bir
tip uyuşmazlığından farklı olarak, bu kusur yalnızca ilgili kod yolu koşturulduğunda ortaya
çıkıyor.

Bu tercih bir ödünç taşıyor: erken doğrulama (kap kurulurken bütün grafiği tarayıp eksik
bağlamaları önceden bulmak) programın en başında çökmeyi göze alır ama hatayı üretime
çıkmadan yakalar; geç doğrulama (bu ölçümün seçtiği yol) kabın kurulmasını hızlı ve basit
tutar ama eksik bir bağ, o kod yolu hiç koşturulmadığı sürece fark edilmeden kalabilir. Bu
ders ikinci yolu modelliyor, çünkü kabın kendisi de kaynakta yazmayan bir karardan ibaret —
`al` yönteminin ne zaman doğrulama yaptığı da, tıpkı bir bağımlılığın nereden geldiği gibi,
yalnız kabın kendi kodunu okuyarak anlaşılabilir bir seçim.

#### Özet

- Yapıcı temelli enjeksiyon, bir bağımlılığı nesne kurulmadan önce çözüp yapıcıya bağımsız
  değişken olarak veren düzenektir; kaynakta bu değeri dolduran hiçbir atama yoktur.
- Aynı sınıf hem kapla hem elle kurulabilir ve ikisi de çalışır — bağımlılık bir arayüze
  bağlı olduğu sürece kap, sınanabilirliği bozmadan araya giriyor.
- Hangi gerçekleştirimin hangi arayüze enjekte edileceği kararı kaynakta gerçekten duran
  bir `bagla` çağrısından geliyor; bu, açıklamadan değil çağrıdan doğan bir davranış.
- Bağlanmamış bir arayüz istendiğinde kap hemen ve adıyla bir istisna fırlatır — bu, dört
  kusurdan tek gürültülü olanı.
- Bu hata kabın kurulduğu satırda değil, bağımlılığın gerçekten istendiği ilk anda düşer;
  kurulma ile doğrulama aynı zamanda olmaz.

#### Sonraki Adım

Bu ders `Servis`'in bağımlılığını nereden aldığını ölçtü, ama `Servis`'in kendisinin kap
tarafından ne zaman yeniden kurulduğunu hiç sormadı — `kap.al(Servis.class)` her
çağrıldığında aynı örneği mi döndürüyor, yoksa her seferinde yeni bir tane mi kuruyor?
`Bilesen` açıklamasının bir `kapsam` alanı var ve bu ders onu hiç kullanmadı; birinci
derste görülen `Servis`'in tekil kalması da, `Istek`'in her istekte yeniden kurulması da
tam bu alanın değerinden geliyordu. Sıradaki ders bu alanı açar: bir kap bileşeni ne zaman
aynı kalır, ne zaman yeniden kurulur ve kurulum yöntemi tam olarak hangi anda çağrılır.

### Bileşen Kapsamları ve Yaşam Döngüsü

Source: https://academia.sh/tr/kurslar/kurumsal-uygulama-cercevesi/bilesen-kapsamlari-ve-yasam-dongusu

Bir kap bileşeninin tekil kalması nesne kabının varsayılan kararı, her istekte yeniden kurulması ise açıkça yazılmış bir kapsam açıklamasından geliyor. Tanınmayan bir kapsam adı hiçbir hata vermiyor — bileşen sessizce tekil olmaktan çıkıyor ve bu ancak iki isteğin kimliğini `==` ile karşılaştırarak görülüyor.

Önceki ders `Bilesen` açıklamasının bir `kapsam` alanı taşıdığını, ama o alanı hiç
kullanmadığını not ederek kapandı: `kap.al(Servis.class)` her çağrıldığında aynı örneği mi
döndürüyor, yoksa her seferinde yeni bir tane mi kuruyor? Bu ders tam bu soruyu açar —
`kapsam` alanının ne yaptığını ve bir kap bileşeninin ne zaman yeniden kurulduğunu ölçer;
ikinci yarısı da bir bileşenin kurulduktan sonra ama kullanılmadan önce çağrılan tek bir
yöntemi — **yaşam döngüsü kancası**nı — inceler.

#### Kapsam Kararının Yeri

`Bilesen` açıklaması bir `kapsam` alanı taşıyor ve varsayılan değeri `"tekil"`. `al`
yöntemi bir tip için bu değeri okuyor ve yalnız `tekil.equals("tekil")` doğruysa
`tekiller` haritasına bakıyor; kapsam başka bir değerse (`"her-istekte"` gibi) bu denetim
hiç yapılmıyor ve kod her seferinde `yapici.newInstance(arg)` satırına düşerek yeni bir
örnek kuruyor. `Servis` sınıfı `@Kap.Bilesen` yazıyor, parametre vermeden — bu, `kapsam()`'ın
varsayılan değerinin uygulanacağı anlamına geliyor. `Istek` sınıfı ise `@Kap.Bilesen(kapsam
= "her-istekte")` yazıyor — açık bir değer.

Bu ayrımın doğrulanma yolu **kimlik**tir, değer eşitliği değil: `kap.al(Servis.class) ==
servis` ifadesi iki referansın aynı bellek konumundaki nesneye işaret edip etmediğini
soruyor. Java'da bunu doğrulamanın başka bir kestirme yolu yok — iki `Servis` nesnesinin
alanları aynı olsa bile `==` yalnız gerçekten aynı nesne olduklarında `true` verir. Bu
dersin ölçümü kimlik karmasını (`hashCode()` çıktısını) hiçbir yerde basmıyor, çünkü o
değer koşumdan koşuma değişebilir ve kendisi bir kanıt değil; kanıt yalnız `==`
karşılaştırmasının sonucu.

- **CK11.** Kapsam denetimi yalnız `tekil` değeri için `tekiller` haritasına bakıyor; başka
  her değer "her seferinde yeniden kur" anlamına geliyor, ayrı bir liste ya da sayaç
  tutulmuyor.
- **CK12.** Kimlik yalnız `==` ile okunuyor; hiçbir nesnenin kimlik karması ya da bellek
  adresi basılmıyor.
- **CK13.** Kurulum yöntemi (`Kur` açıklaması) yapıcı çalıştıktan ve ayar alanları
  dolduktan **sonra** çağrılıyor; nesne zaten tamamen kurulmuş durumda.
- **CK14.** Bir kap bileşeninde en çok bir kurulum yöntemi bulunuyor; bu ölçüm birden çok
  `Kur` işaretli yöntemi olan bir sınıfı denemiyor.
- **CK15.** Tanınmayan bir kapsam adı (`"tanimsiz-kapsam"` gibi) derleme zamanında
  yakalanmıyor — `kapsam()` bir dizgi döndürüyor, kap onu yalnız çalışma zamanında
  `"tekil"`'e eşitliyor.

```java
// Kap.java — bir kap bileseninin kapsamini ve kurulum sirasini kaynakta ne dogurdu: bir aciklama mi, kabin varsayilani mi
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.util.*;

public class Kap {

    @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE)
    @interface Bilesen { String kapsam() default "tekil"; }

    @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD)
    @interface Kur {}

    @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD)
    @interface Ayar { String ad(); String varsayilan(); }

    static int aciklama = 0, varsayilan = 0;
    static int sessiz = 0;

    static void gozle(String davranis, String kaynak) {
        if (kaynak.equals("varsayilan")) varsayilan++; else aciklama++;
        System.out.printf("%-38s %s%n", davranis, kaynak);
    }

    static void kural(String ad, String sonuc) {
        if (sonuc.equals("sessiz")) sessiz++;
        System.out.printf("%-38s %s%n", ad, sonuc);
    }

    static void ozet() {
        System.out.printf("davranis: %d aciklama, %d varsayilan%n", aciklama, varsayilan);
        System.out.printf("kusur   : %d sessiz%n", sessiz);
    }

    final Map<Class<?>, Object> tekiller = new HashMap<>();
    final Map<String, String> ayarlar = new LinkedHashMap<>();
    final List<String> gunluk = new ArrayList<>();

    final Map<Class<?>, Class<?>> baglar = new HashMap<>();

    Kap ayar(String ad, String deger) { ayarlar.put(ad, deger); return this; }

    Kap bagla(Class<?> arayuz, Class<?> gerceklestirim) { baglar.put(arayuz, gerceklestirim); return this; }

    Object al(Class<?> tip) throws Exception {
        if (tip.isInterface()) {
            Class<?> gerceklestirim = baglar.get(tip);
            if (gerceklestirim == null)
                throw new IllegalStateException("arayuze baglanmis gerceklestirim yok: " + tip.getSimpleName());
            return al(gerceklestirim);
        }
        Bilesen b = tip.getAnnotation(Bilesen.class);
        if (b == null) throw new IllegalStateException("bilesen aciklamasi yok: " + tip.getSimpleName());
        boolean tekil = b.kapsam().equals("tekil");
        if (tekil && tekiller.containsKey(tip)) return tekiller.get(tip);

        Constructor<?> yapici = tip.getDeclaredConstructors()[0];
        yapici.setAccessible(true);
        Object[] arg = new Object[yapici.getParameterCount()];
        for (int i = 0; i < arg.length; i++) arg[i] = al(yapici.getParameterTypes()[i]);
        Object ornek = yapici.newInstance(arg);

        for (Field f : tip.getDeclaredFields()) {
            Ayar a = f.getAnnotation(Ayar.class);
            if (a == null) continue;
            f.setAccessible(true);
            f.set(ornek, ayarlar.getOrDefault(a.ad(), a.varsayilan()));
        }
        for (Method m : tip.getDeclaredMethods()) {
            if (m.isAnnotationPresent(Kur.class)) { m.setAccessible(true); m.invoke(ornek); gunluk.add("kur:" + tip.getSimpleName()); }
        }
        if (tekil) tekiller.put(tip, ornek);
        return ornek;
    }

    public static void main(String[] args) throws Exception {
        Kap kap = new Kap().bagla(Depo.class, BellekDepo.class).ayar("depo.baslangic", "dolu");

        Servis servis = (Servis) kap.al(Servis.class);
        Kap.gozle("ayni ornegin donmesi", kap.al(Servis.class) == servis ? "varsayilan" : "aciklama");
        Kap.gozle("her istekte yeni ornek", kap.al(Istek.class) != kap.al(Istek.class) ? "aciklama" : "varsayilan");
        Kap.gozle("kurulum yonteminin cagrilmasi", kap.gunluk.contains("kur:BellekDepo") ? "aciklama" : "varsayilan");

        BellekDepo elle = new BellekDepo();
        System.out.println("kapla kurulanin durumu   : " + ((BellekDepo) servis.depo).durum);
        System.out.println("elle kurulanin durumu    : " + elle.durum);

        Kap k2 = new Kap();
        Object y1 = k2.al(YanlisKapsam.class), y2 = k2.al(YanlisKapsam.class);
        Kap.kural("kapsam adi taninmiyor", y1 == y2 ? "etkisiz" : "sessiz");

        Kap.ozet();
    }
}

interface Depo { String oku(); }

@Kap.Bilesen
class BellekDepo implements Depo {
    @Kap.Ayar(ad = "depo.baslangic", varsayilan = "bos") String baslangic;
    String durum = "kurulmadi";
    @Kap.Kur void hazirla() { durum = "hazir:" + baslangic; }
    public String oku() { return durum; }
}

@Kap.Bilesen
class Servis {
    final Depo depo;
    Servis(Depo depo) { this.depo = depo; }
    String rapor() { return depo.oku(); }
}

@Kap.Bilesen(kapsam = "her-istekte")
class Istek { }

@Kap.Bilesen(kapsam = "tanimsiz-kapsam")
class YanlisKapsam { }
```

```
ayni ornegin donmesi                   varsayilan
her istekte yeni ornek                 aciklama
kurulum yonteminin cagrilmasi          aciklama
kapla kurulanin durumu   : hazir:dolu
elle kurulanin durumu    : kurulmadi
kapsam adi taninmiyor                  sessiz
davranis: 2 aciklama, 1 varsayilan
kusur   : 1 sessiz
```

Bu konunun birinci dersi aynı soruyu on iki davranış ve dört kusur üzerinden ölçmüş,
`3 çağrı, 6 açıklama, 3 varsayılan` ve `1 hata, 2 sessiz, 1 etkisiz` toplamına varmıştı; bu
ders o çekirdeğin yalnız kapsam ve yaşam döngüsü diliminde kalıyor — `bagla` ile enjekte
edilen bağımlılık ve `dene`'nin ölçtüğü bağlanmamış arayüz burada yeniden ölçülmüyor.

#### Tekil, Varsayılanın Kendisi

`ayni ornegin donmesi` satırı `varsayilan` çıkıyor çünkü `Servis`'in kaynak dosyasında
kapsamı belirten hiçbir sözcük yok. `@Kap.Bilesen` çıplak yazılıyor, parantezsiz —
`kapsam()`'ın `default "tekil"` değeri devreye giriyor ve bu değeri veren satır `Servis.java`
değil, `Kap.java`'daki açıklama tanımı. Bu konunun birinci dersinde de görülen bir örüntü
burada tam ortasına oturuyor: kaynağı okuyan biri `Servis`'in tekil olduğunu **bilemez**,
çünkü kaynak bunu hiç söylemiyor; yalnız kabın kuralını (varsayılan kapsamın tekil olduğunu)
önceden bilen biri bu sonucu tahmin edebilir.

`her istekte yeni ornek` satırı ise tam tersi: `açıklama` çıkıyor, çünkü `Istek` sınıfı
`kapsam = "her-istekte"` yazarak varsayılan değeri **açıkça bastırıyor**. Bu, bir Java
yöntem yeniden tanımlaması değil, bir varsayılan değerin bir açıklama bağımsız
değişkeniyle üzerine yazılması — `kap.al(Istek.class) != kap.al(Istek.class)` iki ayrı
çağrının iki ayrı nesne döndürdüğünü doğruluyor ve bu fark kaynakta gerçekten yazan bir
sözcükten geliyor: `"her-istekte"`.

#### Kurulum Yöntemi: Nesne Hazır Olduktan Sonra

`BellekDepo` sınıfının `hazirla()` yöntemi `@Kap.Kur` taşıyor ve gövdesi tek satır: `durum =
"hazir:" + baslangic;`. Bu yöntemin ne zaman çağrılacağı `al` yönteminin sırasından
belirleniyor: önce yapıcı çalışıyor (`ornek` oluşuyor), sonra `Ayar` işaretli alanlar
dolduruluyor (`baslangic` alanına değer yazılıyor), **sonra** `Kur` işaretli yöntemler
çağrılıyor. Bu sıra rastgele değil — `hazirla()` çağrıldığında `baslangic` alanı zaten dolu
olmasaydı, `durum` değeri `"hazir:null"` gibi anlamsız bir şey taşırdı. Kap bu sırayı
zorluyor ve kaynakta bu sırayı belirten hiçbir sayı ya da açıklama parametresi yok — sıra
`al` yönteminin kod satırlarının kendi sırası.

Kurulum yönteminin gerçekten çağrıldığı `kap.gunluk` listesiyle doğrulanıyor:
`gunluk.add("kur:" + tip.getSimpleName())` satırı her çağrıdan sonra bir kayıt bırakıyor ve
`kurulum yonteminin cagrilmasi` satırı bu kaydın varlığını arıyor. Burada gerçek bir saat
ya da sıra numarası tutulmuyor — yalnız "bu yöntem çağrıldı mı" sorusuna cevap veren bir
dizgi listesi. Elle kurulan `BellekDepo` bu kaydı hiç almıyor: `elle.durum` alanı sonsuza
kadar `"kurulmadi"` kalıyor, çünkü `hazirla()`'yı çağıracak taraf yalnız kap ve elle kurulan
nesne kabın hiç görmediği bir nesne.

Kapsam ile kurulum yöntemi birbirinden bağımsız görünen iki mekanizma, ama aralarındaki
ilişki `al` yönteminin kendi sırasından geliyor: kurulum bloğu, tekil önbelleğe yazmadan
**önce** çalışıyor ve bu blok kapsamdan habersiz — hangi kapsamda olursa olsun her **yeni
kurulan** örnek için bir kez koşuyor. `Istek` sınıfının hiçbir `Kur` işaretli yöntemi
olmadığı için bu ölçüm bunu doğrudan göstermiyor, ama düzenek şunu ima ediyor: `her-istekte`
kapsamlı bir bileşenin bir kurulum yöntemi olsaydı, o yöntem her istekte yeniden çağrılırdı
— çünkü `her-istekte` kapsamı her isteği yeni bir kurulmaya çeviriyor ve her kurulma kendi
kurulum çağrısını tetikliyor. Tekil bir bileşende kurulum yöntemi ömür boyu **tam bir kez**
çalışıyor; her-istekte bir bileşende (varsayımsal olarak) her istekte yeniden çalışırdı.
Kapsam ve kurulum yöntemi kaynakta iki ayrı açıklama olarak duruyor, ama aralarındaki bu
ilişki hiçbir yerde yazmıyor — yalnız `al` yönteminin kod sırasını okuyarak çıkarılabiliyor.

#### Neden Ayrı Bir Adım

Kurulum yöntemini yapıcının kendisine koymak neden işe yaramazdı? `BellekDepo`'nun yapıcısı
yok — Java, hiçbir yapıcı yazılmadığında derleyicinin ürettiği parametresiz varsayılan
yapıcıyı kullanıyor. `hazirla()`'yı bu yapıcıya taşımak mümkün değil, çünkü Java'da bir
yapıcının gövdesi yalnız kendi sınıfının yazdığı koddur; kabın `Ayar` alanlarını yapıcıdan
**sonra** doldurduğu düzenekte, yapıcı çalıştığı anda `baslangic` alanı henüz `null`.
Kurulum mantığını yapıcıya koysaydık, o mantık her zaman boş bir değer görürdü.

Bu, kap bileşenlerinin yaşam döngüsünü iki ayrı aşamaya bölmenin gerekçesi: **kurma**
(yapıcının çalışması, nesnenin bellekte var olması) ve **hazırlama** (ayarların
yerleşmesi, bağımlı kaynakların açılması, hazırlık mantığının çalışması). Yapıcı yalnız
birinci aşamayı garanti ediyor; ikinci aşama için ayrı bir kanca gerekiyor ve bu kanca
tam olarak `Kur` açıklamasının işi. Elle kurulan bir nesnede bu ikinci aşama hiç
gerçekleşmiyor — `elle` nesnesi birinci aşamadan geçti (yapıcı çalıştı, nesne var), ama
ikinci aşamaya hiç girmedi.

`Istek` sınıfının hiçbir alanı ve hiçbir kurulum yöntemi yok; her istekte yeni bir örnek
kurulduğunda kap onu `tekiller` haritasına hiç yazmıyor, dolayısıyla hiçbir referans
tutmuyor. Bu ölçümde `Istek` örneklerinin sayısı ayrıca sayılmıyor, çünkü ölçülen şey süre
ya da bellek değil — yalnız iki isteğin aynı nesneyi mi, ayrı nesneleri mi aldığı.

#### Sınırlayıcı Ölçüm: Tanınmayan Bir Kapsam Adı

`YanlisKapsam` sınıfı `@Kap.Bilesen(kapsam = "tanimsiz-kapsam")` taşıyor — kabın tanıdığı
tek değer `"tekil"`, ama açıklama bağımsız değişkeni bir dizgi ve derleyici bu dizginin
kabın anladığı bir değer olup olmadığını denetlemiyor. Sonuç `al` yönteminin `boolean tekil
= b.kapsam().equals("tekil");` satırında ortaya çıkıyor: `"tanimsiz-kapsam".equals("tekil")`
`false` döndürüyor, `tekil` değişkeni `false` oluyor ve kod tekil denetimini hiç yapmadan
her seferinde yeni bir örnek kuruyor.

```
Object y1 = k2.al(YanlisKapsam.class), y2 = k2.al(YanlisKapsam.class);
Kap.kural("kapsam adi taninmiyor", y1 == y2 ? "etkisiz" : "sessiz");
```

`y1 == y2` `false` çıkıyor — iki istek iki ayrı nesne. Bu, kural satırının `"sessiz"`
sınıfına düşmesinin nedeni: hiçbir istisna fırlamıyor, hiçbir günlük satırı "bilinmeyen
kapsam" demiyor, program sorunsuz çalışmaya devam ediyor. Yalnız beklenen davranış
(tekil kalmak) sessizce kayboluyor. Bu kusuru fark etmenin tek yolu, iki isteğin kimliğini
`==` ile karşılaştırmak — değer eşitliğine bakan bir denetim (iki nesnenin alanları aynı mı)
hiçbir şey yakalamaz, çünkü `YanlisKapsam`'ın hiç alanı yok, ikisi de "eşit" görünür. Kusur
ancak kimliğe bakan bir gözle görünüyor ve bu ders boyunca kimlik hep `==` ile okundu,
hiçbir zaman bir kimlik karması basılmadı.

Bu, önceki dersin bağlanmamış arayüz gözlemiyle aynı ailede bir tasarım tercihi: kap orada
da bağımlılık grafiğini kurulurken taramıyor, yalnız istendiğinde denetliyordu. Burada da
kapsam adı `Bilesen` açıklamasına yazıldığı anda değil, `al` çağrıldığı anda okunuyor ve
tanınan tek değer bir dizgi eşitliği ile karşılaştırılıyor — küçük bir yazım farkı (`"tekil"`
yerine `"Tekil"` ya da `"tanimsiz-kapsam"`) derleyiciye hiç görünmeden geçiyor. İki ders de
aynı sonuca varıyor: kabın erken uyarı vermemesi bir eksiklik değil, bu modelin bilinçli
sınırı — doğrulama, kaynağı okumakla değil, kapsanan davranışı gerçekten koşturmakla
mümkün.

#### Özet

- Tekil kapsam kabın varsayılanı; kaynakta bunu belirten hiçbir sözcük olmasa bile
  `Bilesen`'in `kapsam()` değeri devreye giriyor.
- Bir kap bileşenini her istekte yeniden kurdurmak açık bir `kapsam = "her-istekte"`
  bağımsız değişkeni gerektiriyor — bu, kaynakta gerçekten yazan bir sözcükten doğan bir
  davranış.
- Kurulum yöntemi (yaşam döngüsü kancası) yapıcı ve ayar alanları tamamlandıktan sonra, tam
  bir kez çağrılıyor; sırayı belirleyen `al` yönteminin kendi kod sırası.
- Elle kurulan bir nesne kurulum yöntemini hiç almıyor; alan hep başlangıç değerinde
  kalıyor.
- Tanınmayan bir kapsam adı hiçbir hata vermiyor — bileşen sessizce tekil olmaktan çıkıyor
  ve bu ancak `==` ile yapılan bir kimlik karşılaştırmasıyla görünüyor.

#### Sonraki Adım

`hazirla()` yöntemi `baslangic` alanına yazılmış değeri okuyarak `durum` alanını dolduruyor
ve bu `baslangic` alanı da kendi başına bir açıklamayla besleniyor: `@Kap.Ayar(ad =
"depo.baslangic", varsayilan = "bos")`. Bu ders o değerin nereden geldiğini hiç sormadı —
yalnız kurulum sırasında dolu olduğunu varsaydı. Sıradaki ders tam bu soruyu açar: bir kap
bileşeninin ayar değeri, kaynakta hiç görünmeyen bir öncelik sırasından hangi kaynağı
kazanıyor ve bu sıra neden dışarıdan bakan biri için görünmez.

### Yapılandırma Kaynakları

Source: https://academia.sh/tr/kurslar/kurumsal-uygulama-cercevesi/yapilandirma-kaynaklari

Bir ayar değeri üç kaynaktan birinden gelir ve öncelik sırası nesne kabının kuralıdır, kaynakta hiçbir yerde yazmaz: dışarıdan verilen değer bir dosya kaynağını, dosya kaynağı da gömülü varsayılanı bastırır. Yanlış yazılmış bir ayar adı kabı hiç rahatsız etmez — program sessizce gömülü varsayılana düşer ve dolu yerine bos okur.

Önceki ders `hazirla()`'nın `baslangic` alanını okuyarak `durum` değerini doldurduğunu
gösterdi, ama o alanın kendisinin nereden geldiğini hiç sormadı — yalnız kurulum sırasında
dolu olduğunu varsaydı. `@Kap.Ayar(ad = "depo.baslangic", varsayilan = "bos")` satırı iki
şey söylüyor: bu alanın bir dış değeri olabilir ve o değer verilmezse `"bos"` kullanılsın.
Ama "dışarıdan verilen değer" tek bir yerden gelmiyor. Bu ders o kaynağı üçe böler ve
aralarındaki öncelik sırasını ölçer.

Üçüncü ders bir kap bileşeninin kurulumunu üç adıma bölmüştü: yapıcı, ayar alanlarının
doldurulması, kurulum yöntemi — ve bu üç adımın sırası kabın kod satırlarının sırasıydı,
kaynakta yazan bir sayı değil. Bu ders aynı örüntüyü bir düzey daha derine indiriyor: ayar
alanlarının doldurulması kendi içinde tek bir adım değil, kendi sırası olan **üç ayrı
kaynağın** sorgulanması. Önceki dersin "hangi adım önce" sorusu burada "hangi kaynak önce"
sorusuna dönüşüyor ve yanıt yine aynı yerde: kabın kendi kodunda, kaynakta değil.

#### Üç Kaynak, Tek Değer

Bir ayar değeri şu üç yerden birinden gelebilir: **dışarıdan verilen bir değer** (programı
çalıştıran tarafın doğrudan sağladığı, örneğin bir komut satırı bağımsız değişkeni ya da
çalışma zamanı bayrağı), **bir dosya kaynağı** (kap kurulurken okunan, birden çok ayarı
birden taşıyan bir yapılandırma kümesi) ve **gömülü varsayılan** (açıklamanın kendi
`varsayilan()` değeri, sınıfın yazarının koyduğu son çare). Bu üçü aynı anda var olabilir
ve kap her ayar için tek bir kazananı seçmek zorunda.

Kazanan seçimi bir zincir: önce dış kaynağa bakılıyor, orada yoksa dosyaya, orada da yoksa
gömülü varsayılana düşülüyor. Bu zincirin sırası **kabın kuralı**; hiçbir ayar bildirimi
"önce dış kaynağa bak" demiyor, bu davranış `doldur` yönteminin kod satırlarının sırasından
geliyor: `if (disKaynak.containsKey(...)) ... else if (dosya.containsKey(...)) ... else
...`. Bu sıra tersine çevrilseydi (önce dosyaya, sonra dış kaynağa bakılsaydı) aynı üç
kaynak farklı bir kazanan üretirdi — kaynaktaki hiçbir ayar tanımı bu sırayı değiştiremez,
çünkü sıra ayar tanımlarının değil, kabın kendi kodunun içinde.

Bu sıralamanın kendine göre bir mantığı var, ama o mantık da kaynakta yazmıyor: dış kaynak
en yüksek önceliği alıyor çünkü programı çalıştıran taraf her seferinde farklı bir değer
vermek isteyebilir — aynı sınıf değişmeden, iki ayrı koşumda iki ayrı değerle çalışabilsin
diye. Dosya kaynağı ondan bir alt basamakta, çünkü bir kurulumun **tamamına** ait, tek bir
koşuma özgü değil. Gömülü varsayılan en alt basamakta, çünkü sınıfı yazan kişinin "hiçbir
şey verilmezse bu olsun" dediği son çare. Üç basamak da makul bir gerekçeye dayanıyor, ama
üçünü sıralayan gerekçe bir yorum satırı olarak bile kaynakta durmuyor — yalnız `doldur`
yönteminin if-else sırasında gömülü.

`containsKey` denetiminin kendisi de bir ayrıntı taşıyor: bir kaynak, bir anahtara **boş
bir dizgi** değer vermiş olsa bile o kaynak kazanmış sayılıyor, çünkü denetim `null`
kontrolü değil anahtarın **varlığını** soruyor. Bu ölçümde böyle bir durum yok — dört ayarın
hiçbiri boş dizgiyle tanımlı değil — ama düzeneğin kendisi bu ayrımı yapıyor: "değer boş"
ile "değer hiç verilmemiş" iki ayrı durum ve `doldur` yalnız ikincisinde bir sonraki
kaynağa geçiyor.

- **CK16.** Öncelik zinciri sabit üç adım: dış kaynak, dosya, gömülü varsayılan. Bu ölçüm
  dördüncü bir kaynak (örneğin ortam değişkeni) eklemiyor.
- **CK17.** Dış kaynak ve dosya, gerçek bir dosya sisteminden ya da komut satırından
  okunmuyor; ikisi de bellekte tutulan birer `Map<String, String>`.
- **CK18.** Bir ayarın hangi kaynaktan geldiği yalnız `containsKey` denetimiyle
  belirleniyor; anahtarın değeri boş dizgi olsa bile o kaynak "kazanmış" sayılıyor.
- **CK19.** Dört ayar üç farklı senaryoyu temsil ediyor: hem dış kaynakta hem dosyada olan, yalnız
  dosyada olan (iki ayar), hiçbirinde olmayan.
- **CK20.** Kazanan kaynak sayımı bir harita üzerinde tutuluyor; sayaç sıfırdan başlıyor ve
  yalnız bu ölçümün kapsadığı dört ayarı sayıyor.
- **CK21.** Süre, dosya yolu ya da gerçek bir komut satırı bağımsız değişkeni bu ölçümde
  yok; taşınan tek şey ayar adı ve dizgi değeri.

```java
// Yapilandirma.java — uc yapilandirma kaynagi arasindaki oncelik sirasi: dis kaynak, dosya kaynagi, gomulu varsayilan
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.util.*;

public class Yapilandirma {

    @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD)
    @interface Ayar { String ad(); String varsayilan(); }

    static final Map<String, Integer> kazananSayisi = new LinkedHashMap<>();
    static void kazandi(String kaynak) { kazananSayisi.merge(kaynak, 1, Integer::sum); }

    final Map<String, String> disKaynak;
    final Map<String, String> dosya;

    Yapilandirma(Map<String, String> disKaynak, Map<String, String> dosya) {
        this.disKaynak = disKaynak;
        this.dosya = dosya;
    }

    void doldur(Object ornek) throws Exception {
        for (Field f : ornek.getClass().getDeclaredFields()) {
            Ayar a = f.getAnnotation(Ayar.class);
            if (a == null) continue;
            f.setAccessible(true);
            String deger; String kaynak;
            if (disKaynak.containsKey(a.ad())) { deger = disKaynak.get(a.ad()); kaynak = "dis-kaynak"; }
            else if (dosya.containsKey(a.ad())) { deger = dosya.get(a.ad()); kaynak = "dosya"; }
            else { deger = a.varsayilan(); kaynak = "gomulu-varsayilan"; }
            f.set(ornek, deger);
            kazandi(kaynak);
            System.out.printf("%-24s %-6s kazanan: %s%n", a.ad(), deger, kaynak);
        }
    }

    public static void main(String[] args) throws Exception {
        Map<String, String> dis = new LinkedHashMap<>();
        dis.put("depo.baglanti-havuzu", "50");

        Map<String, String> dosya = new LinkedHashMap<>();
        dosya.put("depo.baglanti-havuzu", "10");
        dosya.put("depo.zaman-asimi", "30");
        dosya.put("depo.baslangic.durumu", "dolu");

        Yapilandirma y = new Yapilandirma(dis, dosya);
        AyarliBilesen b = new AyarliBilesen();

        System.out.println("-- her ayarin hangi kaynaktan geldigi --");
        y.doldur(b);

        System.out.println();
        System.out.println("-- kazanan kaynak sayimi --");
        kazananSayisi.forEach((k, v) -> System.out.printf("%-20s %d%n", k, v));

        System.out.println();
        System.out.println("okunan degerler:");
        System.out.println("havuz boyutu     : " + b.havuzBoyutu);
        System.out.println("zaman asimi      : " + b.zamanAsimi);
        System.out.println("baslangic durumu : " + b.baslangicDurumu);
        System.out.println("onbellek boyutu  : " + b.onbellekBoyutu);
    }
}

class AyarliBilesen {
    @Yapilandirma.Ayar(ad = "depo.baglanti-havuzu", varsayilan = "5") String havuzBoyutu;
    @Yapilandirma.Ayar(ad = "depo.zaman-asimi", varsayilan = "10") String zamanAsimi;
    @Yapilandirma.Ayar(ad = "depo.baslangic.durumu", varsayilan = "bos") String baslangicDurumu;
    @Yapilandirma.Ayar(ad = "depo.onbellek.boyutu", varsayilan = "100") String onbellekBoyutu;
}
```

```
-- her ayarin hangi kaynaktan geldigi --
depo.baglanti-havuzu     50     kazanan: dis-kaynak
depo.zaman-asimi         30     kazanan: dosya
depo.baslangic.durumu    dolu   kazanan: dosya
depo.onbellek.boyutu     100    kazanan: gomulu-varsayilan

-- kazanan kaynak sayimi --
dis-kaynak           1
dosya                2
gomulu-varsayilan    1

okunan degerler:
havuz boyutu     : 50
zaman asimi      : 30
baslangic durumu : dolu
onbellek boyutu  : 100
```

#### Dört Ayar, Dört Farklı Kazanan

`depo.baglanti-havuzu` hem dış kaynakta (`50`) hem dosyada (`10`) tanımlı ve kazanan dış
kaynak — zincirin ilk halkası. Bu, bir ayarın kaç yerde tanımlı olduğunun önemli olmadığını
gösteriyor: kazananı belirleyen tek şey, hangi kaynağın zincirde daha önce sorgulandığı.
`depo.zaman-asimi` ve `depo.baslangic.durumu` yalnız dosyada var, dış kaynakta yok; ikisi de
dosyayı kazanıyor çünkü ilk denetim (`disKaynak.containsKey`) `false` dönüp ikinci denetime
geçiyor. `depo.onbellek.boyutu` hiçbir kaynakta yok — zincirin üçüncü halkasına, gömülü
varsayılana düşüyor.

`kazananSayisi` bir `LinkedHashMap` üzerinde tutuluyor, `HashMap` değil — bu ders için
gereken tek şey doğru sayım olsa da, `LinkedHashMap` ekleme sırasını koruduğu için çıktı da
her koşumda aynı sırada basılıyor. Bu küçük seçim bile bir hatırlatma: kabın kendi iç veri
yapısı seçimleri, ölçülen davranışı etkilemese bile çıktının okunabilirliğini etkiliyor ve
bu seçim de kaynaktaki ayar bildirimlerinin hiçbirinde görünmüyor.

Sayım satırı bunu özetliyor: dört ayarın biri dış kaynaktan, ikisi dosyadan, biri gömülü
varsayılandan geldi. Bu sayı **öncelik sırasının etkisinin ölçüsü** — kaç ayarın hangi
kaynağın "kazanmasına" bağlı olduğunu gösteriyor. Kod Olarak Altyapı kursu, dışarıdan
verilen bir değerin kayıt altına alınıp alınmadığını ölçmüştü; bu ders aynı "dışarıdan
verilen değer" fikrini alıyor ama farklı bir soru soruyor — kayıt değil, hangi bileşenin
o değeri gerçekten **gördüğü**.

`f.setAccessible(true)` çağrısı burada da geçiyor, çünkü `AyarliBilesen`'in alanları paket
düzeyinde görünür ve `doldur` yöntemi bu alanlara genel bir `Object` üzerinden erişiyor —
ikinci dersin `Servis` yapıcısında görülen aynı erişim denetimi atlaması, bu kez alanlar
için. Aradaki fark: burada yalnız değer yazılıyor, bir yöntem çağrılmıyor.

Ayar adının kendisi de bir tip değil, yalnız bir dizgi — `String ad()`. Bu, üçüncü dersin
kapsam adı gözlemiyle aynı sınıftan bir seçim: derleyici bir ayar adının gerçekten var olan
bir kaynak anahtarına karşılık gelip gelmediğini denetleyemiyor, çünkü elindeki tek şey
karakter dizisi. `"depo.baglanti-havuzu"` ile `"depo.baglanti_havuzu"` (tire yerine alt
çizgi) derleyici için aynı ölçüde geçerli iki dizgi, ama `containsKey` için tamamen ayrı iki
anahtar. Bu ders dört ayarın hepsinde doğru yazılmış adları kullanıyor; bir sonraki bölüm bu
varsayımı kasıtlı olarak bozuyor.

#### Sıra Değişince Kazanan Değişiyor

Önceki bölümün iddiası — sıranın kabın kendi kodunda gömülü olduğu — düz metinde kalırsa
kanıtlanmamış bir savdan ibaret. Aynı iki kaynağı, aynı iki değeri, yalnız sorgulama
sırasını tersine çevirerek koşturmak bu savı doğruluyor:

```java
// TersSira.java — ayni iki kaynak, sadece sorgulama sirasi degisti, kazanan da degisiyor
import java.util.*;

public class TersSira {
    static String dosyaOncelikli(Map<String, String> disKaynak, Map<String, String> dosya, String ad, String varsayilan) {
        if (dosya.containsKey(ad)) return dosya.get(ad);
        if (disKaynak.containsKey(ad)) return disKaynak.get(ad);
        return varsayilan;
    }

    public static void main(String[] args) {
        Map<String, String> dis = Map.of("depo.baglanti-havuzu", "50");
        Map<String, String> dosya = Map.of("depo.baglanti-havuzu", "10");

        System.out.println("dis-kaynak once sorguladiginda kazanan  : " + dis.get("depo.baglanti-havuzu"));
        System.out.println("dosya once sorguladiginda kazanan       : "
                + dosyaOncelikli(dis, dosya, "depo.baglanti-havuzu", "5"));
    }
}
```

```
dis-kaynak once sorguladiginda kazanan  : 50
dosya once sorguladiginda kazanan       : 10
```

`dis` ve `dosya` haritalarının içeriği bu iki satır arasında hiç değişmedi — `depo.baglanti
-havuzu` anahtarı ikisinde de aynı değerlerle duruyor. Değişen tek şey, `dosyaOncelikli`
yönteminin hangi haritaya **önce** baktığı. `Yapilandirma.doldur` dış kaynağı önce sorguluyor
ve `50` kazanıyor; burada yazılan `dosyaOncelikli` dosyayı önce sorguluyor ve `10` kazanıyor.
Kaynaklardaki veri aynı, ayar adı aynı, bileşenin kendisi hiç yok bu ölçümde — yalnız bir
yöntemin içindeki iki `if` satırının sırası tersine döndü ve kazanan değişti. Bu, öncelik
sırasının kaynaktaki hiçbir ayar bildirimine değil, yalnız kabı yazan kodun kendi sırasına
bağlı olduğunun doğrudan kanıtı.

#### Sınırlayıcı Ölçüm: Yazım Farkı

`depo.baslangic.durumu` anahtarı dosyada `"dolu"` değeriyle duruyor. Bir bileşen bu değeri
okumak istiyor, ama açıklamasına anahtarı yanlış yazıyor — Türkçe `ı` yerine `i` (ya da tam
tersi) kullanıyor:

```java
// YanlisAyarAdi.java — dosyada duran deger, ayar adindaki yazim farki yuzunden hic kazanamiyor
public class YanlisAyarAdi {
    public static void main(String[] args) throws Exception {
        java.util.Map<String, String> dis = java.util.Map.of();
        java.util.Map<String, String> dosya = java.util.Map.of("depo.baslangic.durumu", "dolu");

        Yapilandirma y = new Yapilandirma(dis, dosya);
        YazimFarkli b = new YazimFarkli();
        y.doldur(b);

        System.out.println("dosyada duran deger      : dolu");
        System.out.println("bilesenin okudugu deger  : " + b.durum);
    }
}

class YazimFarkli {
    @Yapilandirma.Ayar(ad = "depo.baslangıc.durumu", varsayilan = "bos") String durum;
}
```

```
depo.baslangıc.durumu    bos    kazanan: gomulu-varsayilan
dosyada duran deger      : dolu
bilesenin okudugu deger  : bos
```

`YazimFarkli` sınıfının `ad` değeri `"depo.baslangıc.durumu"` (noktalı `ı`), dosyadaki anahtar ise
`"depo.baslangic.durumu"` (noktasız `i`) — iki dizgi Java'nın `equals` karşılaştırmasında
birbirinden tamamen farklı, `containsKey` her ikisi için de `false` dönüyor ve `doldur` yöntemi
zincirin üçüncü halkasına düşüyor. Hiçbir istisna yok, hiçbir uyarı yok, hiçbir günlük satırı
"anahtar bulunamadı" demiyor — program sessizce `"bos"` okuyor; dosyada duran `"dolu"` değeri hiç
görülmeden kalıyor.

Bu, ikinci dersteki bağlanmamış arayüz hatasının tam tersi: orada kap **hemen ve gürültülü**
bir istisna fırlatıyordu, burada kap **hiç ses çıkarmıyor**. Öncelik sırası kaynakta hiçbir
yerde yazmadığı gibi, bir kaynağın "kaybetmesi" de kaynakta hiçbir iz bırakmıyor — kaybeden
kaynak sanki hiç var olmamış gibi davranılıyor. Bu kusuru fark etmenin tek yolu, dosyada
duran değer ile bileşenin okuduğu değeri elle karşılaştırmak; kodun kendisi hiçbir zaman bu
farkı işaret etmiyor.

Bu davranışın neden değişmediğine bakmak faydalı: `doldur` yöntemi bir ayar adının dosyada
"neredeyse eşleştiğini" fark edebilecek bir mekanizma taşımıyor, çünkü öyle bir mekanizma
kendi başına yeni bir kusur kaynağı olurdu — hangi yazım farkının "kabul edilebilir"
olduğuna kim karar verecek? Kap bu soruyu hiç sormuyor, yalnız tam eşleşmeyi arıyor. Bu,
üçüncü dersteki tanınmayan kapsam adıyla aynı tasarım tercihi: doğrulamayı erken ve
otomatik yapmak yerine, doğrulamayı tamamen dışarıda bırakıp sonucu davranışla göstermek.
İkisinde de kusur bir istisna değil, beklenenden **farklı ama geçerli görünen** bir çıktı —
`YanlisKapsam`'da iki ayrı nesne, burada `"dolu"` yerine `"bos"`.

Bu iki gözlem — sıra değişince kazananın değişmesi, yazım farkı yüzünden hiçbir kaynağın
kazanamaması — aynı gerçeğin iki yüzü. Birincisinde üç kaynak da doğru adla tanımlıydı ve
sıra değişikliği hangisinin göründüğünü belirledi. İkincisinde sıra hiç değişmedi, ama ayar
adı yanlış yazıldığından zincirin ilk iki halkası hiç sorgulanamadı bile — `containsKey`
sorusu yanlış anahtara soruldu. İki kusur da aynı kökten geliyor: bir ayarın hangi değeri
taşıyacağı, kaynaktaki hiçbir satırdan doğrudan okunamayan, yalnız kabın kuralını ve
kaynakların o an ne içerdiğini bilerek çıkarılabilen bir sonuç.

#### Özet

- Bir ayar değeri üç kaynaktan gelebilir: dışarıdan verilen bir değer, bir dosya kaynağı
  ve açıklamanın gömülü varsayılanı; kazananı belirleyen zincir kabın kendi kod sırası.
- Bir ayarın birden çok kaynakta tanımlı olması önemli değil — yalnız zincirde önce
  sorgulanan kaynak kazanıyor.
- Dört ayardan biri dış kaynaktan, ikisi dosyadan, biri gömülü varsayılandan geldi; bu
  dağılım öncelik sırasının etkisinin doğrudan ölçüsü.
- Öncelik sırasının kendisi kaynakta hiçbir yerde yazmaz; hangi kaynağın kazandığını
  bilmek için kabın kodunu bilmek gerekir.
- Yanlış yazılmış bir ayar adı hiçbir hata vermez — kap sessizce gömülü varsayılana düşer
  ve dosyada duran gerçek değer hiç görülmeden kalır.

#### Sonraki Adım

Dördüncü ayar (`depo.onbellek.boyutu`) hiçbir kaynakta tanımlı değildi ve gömülü varsayılana
düştü — ama bu, kabın "hiçbir şey yapmadığı" anlamına gelmiyordu, yalnız zincirin son
halkasına ulaştığı anlamına geliyordu. Bazı durumlarda kabın kendisi, bir bileşenin hiç
bildirilmediğini fark edip **kendi** varsayılanını devreye sokuyor — bir ayar değeri değil,
bir bileşenin kendisi. Sıradaki ders bu farklı türden varsayılanı ölçer: bir bileşen hiç
yazılmadığında kap ne yapıyor ve bu karar neden kaynakta "koşullu" bir sözcükle hiç
görünmüyor.

### Otomatik Yapılandırma

Source: https://academia.sh/tr/kurslar/kurumsal-uygulama-cercevesi/otomatik-yapilandirma

Bir arayüz için hiçbir bileşen bildirilmemişse nesne kabı kendi varsayılan gerçekleştirimini koyar; kullanıcı kendi bileşenini bildirdiğinde kap sessizce geri çekilir ve kendi varsayılanını hiç kurmaz. Otomatik olan şey koşulludur ve o koşul kaynakta hiçbir sözcükle görünmez.

Önceki ders bir ayarın hiçbir kaynakta bulunmadığında gömülü varsayılana düştüğünü ölçtü —
ama düşen şey bir **değerdi**, önceden var olan bir alanın içeriği. Bu ders aynı fikri bir
düzey yukarı taşıyor: bazen düşen şey bir değer değil, **bir bileşenin kendisi**. Kap, bir
arayüz için hiç kimse bir gerçekleştirim bildirmediğinde, o arayüzü kendi başına
dolduruyor — kendi seçtiği bir sınıfı devreye sokarak. Bu davranışın adı **otomatik
yapılandırma** ve bu dersin sorusu şu: "otomatik" olan şey aslında ne kadar koşullu?

Dördüncü dersin öncelik zinciri üç kaynağı **sırayla** sorguluyordu — dış kaynak, dosya,
gömülü varsayılan — ve her ayar için bu üçünden yalnız biri kazanıyordu. Bu dersin sorduğu
soru farklı bir biçimde: burada sıralı bir zincir yok, yalnız **ikili** bir karar var —
kullanıcı bir şey bildirdi mi, bildirmedi mi. Bu ikilik, otomatik yapılandırmayı önceki
dersten ayıran şey: kazananı belirleyen bir öncelik sırası değil, bir **varlık
sınaması**. Kullanıcının bildirdiği kümede aranan arayüzü karşılayan bir sınıf varsa kap
hiç araya girmiyor; yoksa kendi elindeki tek adayı devreye sokuyor.

#### Bildirilmemiş Bir Arayüz, Kabın Kendi Kararı

Aşağıdaki ölçümde üç arayüz var — `Bildirimci`, `Onbellek`, `Metrik` — ve her biri için kabın
kendi elinde bir **otomatik varsayılan** sınıfı hazır bulunuyor: `OtomatikBildirimci`,
`OtomatikOnbellek`, `OtomatikMetrik`. Bu ölçüm önceki derslerin `@Kap.Bilesen` işaretini
kullanmıyor — burada "bildirmek" yalnız bir sınıfın kullanıcı tarafından verilen küçük bir
listeye eklenmesi anlamına geliyor, ama rolü aynı: bu liste, kabın kendi kararıyla
kuracağı kap bileşenlerinin yerini alacak adayları taşıyor. Kap bir arayüz için bir karar
vermeden önce kullanıcının o arayüzü gerçekleştiren bir bileşen bildirip bildirmediğine
bakıyor. Bildirmiş
mi? O zaman kap **geri çekiliyor** — kendi otomatik varsayılanını hiç kurmuyor. Bildirmemiş
mi? Kap kendi varsayılanını koyuyor ve program bu kararı hiçbir yerde açıkça istemedi.

Bu "bakma" işlemi `coz` yönteminde oluyor: yöntem, kullanıcının bildirdiği bileşenler
listesini geziyor, listede aranan arayüzü gerçekleştiren bir sınıf varsa onu döndürüyor;
yoksa kendi sayacını artırıp otomatik varsayılanı kuruyor. Bu ölçümde "kullanıcının
bildirdiği bileşenler listesi" gerçek bir sınıf yolu taraması değil, elle verilen küçük bir
liste — ama düzeneğin kendisi aynı: gerçek bir uygulama çerçevesi de aynı soruyu, yalnız
çok daha büyük bir aday kümesi üzerinde soruyor.

Denetim `arayuz.isAssignableFrom(aday)` ile yapılıyor, `instanceof` ile değil — çünkü elde
henüz bir **nesne** yok, yalnız bir `Class<?>` referansı var. `isAssignableFrom`, bir tipin
başka bir tipe atanabilir olup olmadığını, hiçbir örnek kurmadan, yalnız iki `Class`
nesnesini karşılaştırarak soruyor. Bu ayrım önemli: kap, hangi sınıfın hangi arayüzü
gerçekleştirdiğine karar vermek için önce o sınıfı **kurmak** zorunda değil — soruyu
tamamen tip düzeyinde yanıtlıyor ve yalnız kazanan aday belli olduktan sonra
`getDeclaredConstructor().newInstance()` ile gerçek nesneyi kuruyor. Otomatik varsayılan
adayları da aynı yolla kuruluyor; aralarındaki tek fark, otomatik varsayılanın yalnız
**hiçbir aday bulunamadığında** çağrılması.

- **CK22.** "Kullanıcının bildirdiği bileşenler" burada elle verilen küçük bir liste;
  gerçek bir sınıf yolu taraması ya da paket keşfi bu ölçümde yok. Üç arayüzün de tam
  olarak bir otomatik varsayılanı var; birden çok otomatik aday arasında seçim yapma bu
  ölçümün kapsamında değil.
- **CK23.** Kap iki kurulumu aynı çalıştırma içinde, art arda koşturuyor; her kurulum kendi
  bileşen listesini kendi taşıyor, aralarında paylaşılan durum yok.
- **CK24.** "Hiçbir açıklama olmadan doğan davranış" sayacı yalnız otomatik varsayılanın
  kurulduğu durumları sayıyor; kullanıcının kendi bildirdiği bir bileşen bu sayaca hiç
  girmiyor.
- **CK25.** Bir sınıfın kaç kez kurulduğu, sınıfın kendi yapıcısına eklenen bir sayaçla
  izleniyor; gerçek bir zaman damgası ya da bellek ölçümü kullanılmıyor.

Bu ölçüm önceki üç dersin `Kap`'ından bilinçli olarak daha dar: `coz` yöntemi
`getDeclaredConstructor().newInstance()` çağırıyor, parametresiz bir yapıcı arıyor —
ikinci dersteki `al` yöntemi gibi yapıcı parametrelerini özyinelemeli çözmüyor. Bu ölçümdeki
dört sınıfın (`OtomatikBildirimci`, `OtomatikOnbellek`, `OtomatikMetrik`, `OzelBildirimci`)
hiçbirinin bağımlılığı yok, bu yüzden bu basitleştirme sonucu değiştirmiyor; ama gerçek bir
uygulama çerçevesinde otomatik yapılandırılan bir bileşenin de kendi bağımlılıkları
olabilir ve o zaman kurulum süreci bu dersin `coz`'u ile ikinci dersin `al`'ının
birleşimine ihtiyaç duyardı.

```java
// OtomatikYapilandirma.java — bir bilesen bildirilmemisse kap kendi varsayilanini koyuyor, bildirilmisse geri cekiliyor
import java.util.*;

public class OtomatikYapilandirma {

    static int hicAciklamasizDoganSayisi = 0;

    static Object coz(Class<?> arayuz, List<Class<?>> kullaniciBilesenleri, Class<?> otomatikVarsayilan) throws Exception {
        for (Class<?> aday : kullaniciBilesenleri) {
            if (arayuz.isAssignableFrom(aday)) {
                System.out.printf("%-12s kullanicinin bildirdigi %-18s kap geri cekildi%n", arayuz.getSimpleName(), aday.getSimpleName());
                return aday.getDeclaredConstructor().newInstance();
            }
        }
        hicAciklamasizDoganSayisi++;
        System.out.printf("%-12s hicbir bildirim yok, kap varsayilanini koydu: %s%n", arayuz.getSimpleName(), otomatikVarsayilan.getSimpleName());
        return otomatikVarsayilan.getDeclaredConstructor().newInstance();
    }

    static void kurulum(String ad, List<Class<?>> kullaniciBilesenleri) throws Exception {
        System.out.println("-- kurulum: " + ad + " --");
        hicAciklamasizDoganSayisi = 0;
        Bildirimci b = (Bildirimci) coz(Bildirimci.class, kullaniciBilesenleri, OtomatikBildirimci.class);
        Onbellek o = (Onbellek) coz(Onbellek.class, kullaniciBilesenleri, OtomatikOnbellek.class);
        Metrik m = (Metrik) coz(Metrik.class, kullaniciBilesenleri, OtomatikMetrik.class);
        System.out.println("bildirimci sonucu    : " + b.gonder());
        System.out.println("onbellek sonucu      : " + o.getir("x"));
        System.out.println("metrik sonucu        : " + m.say());
        System.out.println("hicbir aciklama olmadan dogan davranis sayisi: " + hicAciklamasizDoganSayisi);
        System.out.println();
    }

    public static void main(String[] args) throws Exception {
        kurulum("hicbir kullanici bileseni yok", List.of());
        kurulum("kullanici kendi bildirimcisini yazdi", List.of(OzelBildirimci.class));

        System.out.println("iki kurulum boyunca OtomatikBildirimci kac kez kuruldu: " + OtomatikBildirimci.kurulmaSayisi);
    }
}

interface Bildirimci { String gonder(); }
interface Onbellek { String getir(String anahtar); }
interface Metrik { int say(); }

class OtomatikBildirimci implements Bildirimci {
    static int kurulmaSayisi = 0;
    OtomatikBildirimci() { kurulmaSayisi++; }
    public String gonder() { return "otomatik-bildirim"; }
}
class OtomatikOnbellek implements Onbellek {
    public String getir(String anahtar) { return "otomatik-onbellek:" + anahtar; }
}
class OtomatikMetrik implements Metrik {
    public int say() { return 0; }
}

class OzelBildirimci implements Bildirimci {
    public String gonder() { return "ozel-bildirim"; }
}
```

```
-- kurulum: hicbir kullanici bileseni yok --
Bildirimci   hicbir bildirim yok, kap varsayilanini koydu: OtomatikBildirimci
Onbellek     hicbir bildirim yok, kap varsayilanini koydu: OtomatikOnbellek
Metrik       hicbir bildirim yok, kap varsayilanini koydu: OtomatikMetrik
bildirimci sonucu    : otomatik-bildirim
onbellek sonucu      : otomatik-onbellek:x
metrik sonucu        : 0
hicbir aciklama olmadan dogan davranis sayisi: 3

-- kurulum: kullanici kendi bildirimcisini yazdi --
Bildirimci   kullanicinin bildirdigi OzelBildirimci     kap geri cekildi
Onbellek     hicbir bildirim yok, kap varsayilanini koydu: OtomatikOnbellek
Metrik       hicbir bildirim yok, kap varsayilanini koydu: OtomatikMetrik
bildirimci sonucu    : ozel-bildirim
onbellek sonucu      : otomatik-onbellek:x
metrik sonucu        : 0
hicbir aciklama olmadan dogan davranis sayisi: 2

iki kurulum boyunca OtomatikBildirimci kac kez kuruldu: 1
```

#### Aynı Kap, İki Kurulum, İki Ayrı Sayı

İlk kurulumda kullanıcı hiçbir bileşen bildirmiyor — `kurulum("hicbir kullanici bileseni
yok", List.of())` boş bir liste veriyor. Üç arayüzün üçü de kabın otomatik varsayılanlarını
alıyor: `OtomatikBildirimci`, `OtomatikOnbellek`, `OtomatikMetrik`. Sayaç `3` — üç davranışın
üçü de hiçbir açıklama olmadan doğdu. Kaynakta `OtomatikBildirimci` adını anan tek bir satır
yok; kap bu kararı kendi başına verdi, çünkü sorduğu soruya ("kullanıcı bir şey bildirdi mi")
yanıt `hayır` çıktı.

İkinci kurulumda kullanıcı yalnız `Bildirimci` için kendi bileşenini bildiriyor —
`OzelBildirimci`. Şimdi üç sonuçtan biri değişiyor: `Bildirimci` isteği artık `OzelBildirimci`
ile karşılanıyor, çıktı `"ozel-bildirim"` oluyor ve kap geri çekildiğini açıkça söylüyor
("kap geri cekildi"). Ama `Onbellek` ve `Metrik` hâlâ hiçbir bildirim almadı, bu yüzden
onlar hâlâ otomatik varsayılanlarını alıyor. Sayaç bu kez `2` — üç davranıştan ikisi hâlâ
hiçbir açıklama olmadan doğuyor, biri artık kaynakta duran bir bildirimden geliyor.

Bu iki sayı — `3` ve `2` — aynı `coz` yönteminin, aynı üç arayüzle, yalnız bir girdi
farkıyla ürettiği iki ayrı sonuç. Kap'ın kendisi hiç değişmedi; değişen tek şey kullanıcının
verdiği liste. "Otomatik" sözcüğü burada "her zaman aynı" anlamına gelmiyor — **koşullu**
anlamına geliyor: koşul karşılanmazsa (kimse bir şey bildirmemişse) otomatik varsayılan
devreye giriyor, koşul karşılanırsa (biri kendi bileşenini bildirmişse) otomatik varsayılan
hiç kurulmuyor.

Kurulumun kendisi de bir gözlem taşıyor: `kurulum` yöntemi çağrılmadan önce `hicAciklamasizDoganSayisi`
sıfırlanıyor, çünkü sayaç `static` — iki kurulum aynı statik alanı paylaşıyor ve sıfırlama
olmasaydı ikinci kurulumun sayısı ilkinin üzerine eklenirdi. Bu, ölçümün kendi iç ayrıntısı,
ama tanıdık bir uyarı taşıyor: paylaşılan durum, art arda koşan iki senaryoyu birbirine
karıştırabilir; burada bu karışma bilerek `hicAciklamasizDoganSayisi = 0;` satırıyla
önleniyor, ama `OtomatikBildirimci.kurulmaSayisi` için aynı önlem alınmıyor — o sayaç
bilerek iki kurulum boyunca birikiyor, çünkü ölçülmek istenen tam olarak bu: toplamda kaç
kez kurulduğu.

Bu koşulluluk, otomatik yapılandırmanın asıl vaadi ile asıl riskini aynı anda taşıyor.
Vaat şu: sık kullanılan bir arayüz için hiçbir şey yazmadan makul bir gerçekleştirim elde
etmek — üç satırlık bir kurulumdan üç çalışan bileşen çıkıyor, kimse `OtomatikBildirimci`
adını bilmek zorunda kalmadan. Risk de aynı yerden geliyor: kap hangi koşulda geri
çekileceğine kendi başına karar veriyor ve bu koşul (`isAssignableFrom` denetiminin true
mu false mu döndüğü) kodun hiçbir satırında açıkça yazmıyor. Bir bileşen eklemek yalnız
"yeni bir şey kurmak" değil, aynı zamanda "eskisinin yerini almak" da olabiliyor ve bu
ikincisi kaynakta görünmüyor.

#### Sınırlayıcı Ölçüm: Sessiz Devre Dışı Bırakma

Son satır bu geri çekilmenin ne kadar tam olduğunu gösteriyor: `OtomatikBildirimci` sınıfının
yapıcısı kendi kurulma sayısını tutuyor ve iki kurulum boyunca bu sayı yalnız `1` —
`OtomatikBildirimci` yalnız birinci kurulumda kuruldu, ikinci kurulumda **hiç kurulmadı**.
Kap onu inşa etmeye bile teşebbüs etmedi; `coz` yöntemi `OzelBildirimci`'yi bulduğu anda
döngüden çıktı ve otomatik varsayılana hiç uğramadı.

Bu, dersin sınırlayıcı iddiası: **bir bileşen eklemek, kaynakta hiç anılmayan başka bir
bileşeni sessizce devre dışı bırakabilir.** `OzelBildirimci`'yi ekleyen kişi muhtemelen
yalnız kendi bildirim mantığını istiyordu; `OtomatikBildirimci`'nin var olduğunu ya da artık
kurulmadığını bilmek zorunda değil, çünkü ikisi de aynı `Bildirimci` arayüzü arkasında
duruyor ve çağıran taraf hangisinin çalıştığını bilmeden `gonder()` çağırabiliyor. Devre
dışı bırakma hiçbir uyarı, hiçbir günlük satırı, hiçbir istisna üretmiyor — yalnız bir
sınıfın hiç kurulmaması. `Onbellek` ve `Metrik` bu ikinci kurulumda hâlâ otomatik
kaldı, ama bu da kaynakta hiçbir yerde açıkça yazmıyor; yalnız "kimse bir şey bildirmedi"
sessizliğinin doğal sonucu.

Bu kusurun dördüncü dersin yazım farkından farkı, bir şeyin **yanlış yazılmış olması**
gerekmemesi. `OzelBildirimci` doğru yazıldı, doğru arayüzü gerçekleştirdi, beklendiği gibi
çalıştı — kusur onun kendi kodunda değil, onun **varlığının** başka bir sınıfın hiç
kurulmamasına yol açmasında. İkinci ve üçüncü derslerde görülen kusurlar (bağlanmamış
arayüz, tanınmayan kapsam adı) birer eksiklikti; buradaki "kusur" tam tersi — bir şeyin
**eklenmesi**. Kurs boyunca ölçülen üç kaynaktan (çağrı, açıklama, varsayılan) hiçbiri tek
başına "iyi" ya da "kötü" değil; bu ders otomatik varsayılanın rahatlığının, aynı anda bir
görünmezlik bedeli taşıdığını gösteriyor.

#### Üç Bağımsız Karar, Tek Yöntem

`coz` yöntemi her arayüz için ayrı ayrı çağrılıyor ve her çağrı kendi kararını kendi başına
veriyor — `Bildirimci` için verilen karar `Onbellek` ya da `Metrik` için verilen kararı hiç
etkilemiyor. Bu bağımsızlık kasıtlı: gerçek bir uygulama çerçevesinde de onlarca otomatik
yapılandırma adayı aynı anda değerlendirilir ve her biri kendi arayüzü için bağımsız bir
"kullanıcı bildirdi mi" sorusuna yanıt verir. Bu ölçümün üç arayüzü, bu bağımsızlığı görünür
kılmak için seçildi: ikinci kurulumda yalnız `Bildirimci` değişti, ötekiler aynı kaldı —
eğer bu üçü birbirine bağlı olsaydı, `Bildirimci`'yi değiştirmek `Onbellek`'in de otomatik
kalıp kalmayacağını etkileyebilirdi. Bu ölçümde etkilemiyor, çünkü `coz` çağrıları birbirini
hiç görmüyor.

Bu bağımsızlık aynı zamanda bir sınır çiziyor: `coz` yöntemi, kullanıcının bildirdiği
listede **birden çok** aday bulursa ne yapacağını hiç sormuyor — döngü ilk eşleşen adayı
bulduğu anda geri dönüyor, listenin geri kalanına hiç bakmıyor. Bu ölçümde her arayüz için
en çok bir kullanıcı adayı var, bu yüzden bu durum hiç ortaya çıkmıyor; ama gerçek bir
sistemde iki ayrı bileşenin aynı arayüzü gerçekleştirip ikisinin de bildirilmesi mümkün ve
o zaman kazananı belirleyen şey artık "kullanıcı bildirdi mi" sorusu değil, listenin
**sırası** olurdu — dördüncü dersteki zincirin sıra bağımlılığına çok benzer bir kusur,
farklı bir düzeneğin içinde.

#### Özet

- Bir arayüz için hiçbir bileşen bildirilmediğinde kap kendi otomatik varsayılanını
  kuruyor; bu karar kaynakta hiçbir satırda yazmıyor.
- Kullanıcı kendi bileşenini bildirdiğinde kap geri çekiliyor ve otomatik varsayılanı hiç
  kurmuyor — iki davranış aynı `coz` yönteminden, yalnız girdi farkıyla çıkıyor.
- Aynı kap iki kurulumda koşturulduğunda hiçbir açıklama olmadan doğan davranış sayısı
  `3`'ten `2`'ye düşüyor; bu fark yalnız kullanıcının bildirdiği bir bileşenle açıklanıyor.
- "Otomatik" olan şey aslında koşullu: koşul (kimsenin bir şey bildirmemiş olması)
  kaynakta hiçbir sözcükle görünmüyor.
- Bir bileşen eklemek, kaynakta hiç anılmayan başka bir bileşeni sessizce devre dışı
  bırakabilir — devre dışı kalan sınıf hiç kurulmuyor, hiçbir uyarı üretmiyor.

Bu konunun beş dersi boyunca aynı üç kaynak tekrar tekrar ortaya çıktı: bir davranış ya
kaynakta duran bir çağrıdan, ya kaynaktaki bir açıklamadan, ya da kabın kendi kuralından —
bir varsayılandan — doğdu. Bu ders o üçüncü kaynağın kendi içinde de bir kademesi
olduğunu gösterdi: bazen varsayılan sabit bir değer (dördüncü ders), bazen koşullu bir
bileşen seçimi (bu ders). İkisinin ortak noktası aynı: kaynağı yukarıdan aşağı okuyan biri,
kabın kuralını önceden bilmeden, hangi sonuca varılacağını kestiremez.

#### Sonraki Adım

İki kurulumda da `Onbellek` ve `Metrik` çağrı yerine hiç dokunulmadan, aynı arayüz
üzerinden çalıştı — çağıran taraf hangi gerçekleştirimin arkada durduğunu hiçbir zaman
bilmek zorunda kalmadı. Bu konunun son dersi tam bu gözlemi bir adım öteye taşır: bir
bileşenin davranışına, çağrı yerine hiç dokunmadan, dışarıdan bir şey eklemek mümkün mü?
Sıradaki ders bunu ölçer — kesişen bir ilginin (günlükleme, sayaç, denetim gibi tek bir
sınıfa ait olmayan bir davranışın) çağrı yerini değiştirmeden nasıl eklendiğini.

### Görünüş Odaklı Programlama

Source: https://academia.sh/tr/kurslar/kurumsal-uygulama-cercevesi/gorunus-odakli-programlama

Bir kap bileşenine konan bir sarmalama işareti, çağrı yerine hiç dokunmadan davranış ekliyor: kesişen bir ilgi (bu örnekte bir sayaç) her çağrıda araya giriyor ve düzenek bir vekildir. Ama bu işaretin var olması onun uygulanabilir olduğu anlamına gelmiyor — arayüzü olmayan bir sınıfa konan aynı işaret hiçbir hata vermeden etkisiz kalıyor.

Önceki beş ders bir kap bileşeninin nasıl kurulduğunu, hangi bağımlılıkla donatıldığını, ne
kadar yaşadığını, ayarlarını nereden aldığını ve hangi gerçekleştirimin otomatik seçildiğini
ölçtü — hepsi bileşenin **kendi** iç hâliyle ilgiliydi. Önceki dersin `Onbellek` ve `Metrik`
örnekleri çağrı yerine hiç dokunulmadan çalıştı, ama çağıran taraf yine de her zaman
**gerçek işi yapan** nesneyle konuşuyordu. Bu ders farklı bir soru soruyor: bir bileşenin
**davranışına**, onun kaynak kodunu hiç değiştirmeden, dışarıdan bir şey eklemek mümkün mü?
Sunucu Temelleri kursu bu tür bir davranışı — birden çok sınıfa dağılmış, tek bir sınıfa ait
olmayan bir ilgiyi — **kesişen ilgi** diye adlandırmıştı: günlükleme, sayaç tutma, yetki
denetimi gibi, iş mantığının kendisiyle ilgisi olmayan ama ona eşlik eden davranışlar. Bu
ders o kesişen ilgiyi, çağrı yerine hiç dokunmadan, bir kap bileşenine nasıl eklediğimizi
ölçer.

#### Sarmalama: Çağrı Yerine Dokunmadan Araya Girmek

`BellekDepo` sınıfı `@Kap.Sarmala` taşıyor. Kap bir bileşeni kurarken bu açıklamayı görürse
ve sınıf tam olarak bir arayüz gerçekleştiriyorsa, kurulan nesneyi doğrudan döndürmüyor —
onun yerine bir **vekil** (proxy) döndürüyor. Vekil, `Depo` arayüzünü uygulayan ama gerçek
işi yapmayan, her çağrıyı önce kendi üzerinden geçiren bir ara nesne: `Proxy.
newProxyInstance` çağrısı, `BellekDepo` örneğini (`hedef`) saran ve her yöntem çağrısında
`sarmaCagrisi++` yapıp sonra çağrıyı gerçek nesneye ileten bir işleyici kuruyor. `Servis`
sınıfının kendisi bu vekili hiç bilmiyor — `depo.oku()` çağırdığında elindeki referansın
gerçek bir `BellekDepo` mu yoksa onu saran bir vekil mi olduğunu ayırt edemiyor, çünkü
ikisi de aynı `Depo` arayüzünü taşıyor.

Bu, kesişen bir ilginin çağrı yerine hiç dokunmadan eklenmesinin somut hâli: `Servis.rapor()`
yöntemi hâlâ `depo.oku()` çağırıyor, tek bir karakter bile değişmedi, ama artık her çağrı bir
sayaç artışından geçiyor. Sayacı ekleyen şey ne `Servis`'in kaynak kodu ne `BellekDepo`'nun
`oku()` gövdesi — ekleyen şey `Sarmala` işaretini gören kabın kendisi. Bu, önceki dersin
otomatik yapılandırmasıyla aynı aileden bir düzenek: orada kap bir arayüz için hangi
sınıfın kurulacağına karar veriyordu, burada kap kurulan nesnenin **etrafına** ne
sarılacağına karar veriyor. İkisi de kaynakta iz bırakmayan bir kap kararı.

`Proxy.newProxyInstance` çağrısına verilen üçüncü bağımsız değişken bir işleyici —
`(p, m, a) -> { sarmaCagrisi++; return m.invoke(hedef, a); }`. Bu üç harfli parametre
kısaltması standart bir sözleşmeden geliyor: `p` çağrının yapıldığı vekil nesnenin kendisi,
`m` çağrılan yöntemi temsil eden bir `Method` nesnesi, `a` da o çağrıya verilen bağımsız
değişkenler dizisi. Vekile yapılan **her** çağrı — `oku()` olsun, ileride eklenecek başka
bir yöntem olsun — bu tek işleyiciden geçiyor, çünkü `Proxy.newProxyInstance` çağrı
başına ayrı bir yöntem üretmiyor, tek bir yakalayıcı kuruyor ve hangi yöntemin çağrıldığını
`m` parametresiyle ayırt ediyor. Bu ölçümde `Depo` arayüzünün tek bir yöntemi var
(`oku()`), bu yüzden `m`'nin değeri her zaman aynı; ama düzenek arayüze kaç yöntem
eklenirse eklensin aynı kalıyor.

- **CK26.** Vekil yalnız çağrı sayısını artırıyor, gerçek çağrıyı `m.invoke(hedef, a)` ile
  hedefe iletiyor; hiçbir çağrı vekil tarafından engellenmiyor ya da değiştirilmiyor.
- **CK27.** Sarmalama yalnız tam olarak bir arayüz gerçekleştiren sınıflara uygulanıyor
  (`tip.getInterfaces().length == 1`); bu, `Proxy.newProxyInstance`'ın kendi kısıtı değil,
  bu ölçümün kendi basitleştirmesi. Birden çok arayüz gerçekleştiren bir sınıf bu ölçümde
  denenmiyor.
- **CK28.** Sayaç yalnız çağrı **sayısını** tutuyor; hangi iş parçacığının çağırdığı, ne
  kadar sürdüğü ya da hangi bağımsız değişkenlerle çağrıldığı bu ölçümde yok.
- **CK29.** Elle kurulan bir nesne kabın `al` yöntemine hiç girmiyor, bu yüzden vekil
  mantığına da hiç uğramıyor — sarmalama yalnız kabın kurduğu nesnelerde geçerli.
- **CK30.** Kimlik yalnız `Proxy.isProxyClass` ile okunuyor; hiçbir vekil örneğinin kimlik
  karması basılmıyor.

```java
// Kap.java — bir sarmalama isaretinin bir vekil araciligiyla davranis eklemesini kaynak hic degismeden nasil dogurdugu
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.util.*;

public class Kap {

    @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE)
    @interface Bilesen {}

    @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE)
    @interface Sarmala {}

    static int aciklama = 0, varsayilan = 0;
    static int etkisiz = 0;

    static void gozle(String davranis, String kaynak) {
        if (kaynak.equals("varsayilan")) varsayilan++; else aciklama++;
        System.out.printf("%-38s %s%n", davranis, kaynak);
    }

    static void kural(String ad, String sonuc) {
        if (sonuc.equals("etkisiz")) etkisiz++;
        System.out.printf("%-38s %s%n", ad, sonuc);
    }

    static void ozet() {
        System.out.printf("davranis: %d aciklama, %d varsayilan%n", aciklama, varsayilan);
        System.out.printf("kusur   : %d etkisiz%n", etkisiz);
    }

    int sarmaCagrisi = 0;
    final Map<Class<?>, Class<?>> baglar = new HashMap<>();

    Kap bagla(Class<?> arayuz, Class<?> gerceklestirim) { baglar.put(arayuz, gerceklestirim); return this; }

    Object al(Class<?> tip) throws Exception {
        if (tip.isInterface()) {
            Class<?> gerceklestirim = baglar.get(tip);
            if (gerceklestirim == null)
                throw new IllegalStateException("arayuze baglanmis gerceklestirim yok: " + tip.getSimpleName());
            return al(gerceklestirim);
        }
        Bilesen b = tip.getAnnotation(Bilesen.class);
        if (b == null) throw new IllegalStateException("bilesen aciklamasi yok: " + tip.getSimpleName());

        Constructor<?> yapici = tip.getDeclaredConstructors()[0];
        yapici.setAccessible(true);
        Object[] arg = new Object[yapici.getParameterCount()];
        for (int i = 0; i < arg.length; i++) arg[i] = al(yapici.getParameterTypes()[i]);
        Object ornek = yapici.newInstance(arg);

        if (tip.isAnnotationPresent(Sarmala.class) && tip.getInterfaces().length == 1) {
            Object hedef = ornek;
            ornek = Proxy.newProxyInstance(tip.getClassLoader(), tip.getInterfaces(),
                    (p, m, a) -> { sarmaCagrisi++; return m.invoke(hedef, a); });
        }
        return ornek;
    }

    public static void main(String[] args) throws Exception {
        Kap kap = new Kap().bagla(Depo.class, BellekDepo.class);

        Servis servis = (Servis) kap.al(Servis.class);
        int oncekiSarma = kap.sarmaCagrisi;
        servis.rapor();
        Kap.gozle("cagrinin sarmalanmasi", kap.sarmaCagrisi > oncekiSarma ? "aciklama" : "varsayilan");

        BellekDepo elle = new BellekDepo();
        Kap.gozle("elle kurulan nesnede sarmalama", Proxy.isProxyClass(elle.getClass()) ? "aciklama" : "varsayilan");

        Object arayuzsuz = kap.al(ArayuzsuzBilesen.class);
        Kap.kural("sarmalama arayuzsuz sinifa konuldu", arayuzsuz instanceof Proxy ? "hata" : "etkisiz");

        Kap.ozet();
    }
}

interface Depo { String oku(); }

@Kap.Bilesen @Kap.Sarmala
class BellekDepo implements Depo {
    public String oku() { return "veri"; }
}

@Kap.Bilesen
class Servis {
    final Depo depo;
    Servis(Depo depo) { this.depo = depo; }
    String rapor() { return depo.oku(); }
}

@Kap.Bilesen @Kap.Sarmala
class ArayuzsuzBilesen { String selam() { return "selam"; } }
```

```
cagrinin sarmalanmasi                  aciklama
elle kurulan nesnede sarmalama         varsayilan
sarmalama arayuzsuz sinifa konuldu     etkisiz
davranis: 1 aciklama, 1 varsayilan
kusur   : 1 etkisiz
```

Bu konunun birinci dersi aynı soruyu on iki davranış ve dört kusur üzerinden ölçmüş,
`3 çağrı, 6 açıklama, 3 varsayılan` ve `1 hata, 2 sessiz, 1 etkisiz` toplamına varmıştı; bu
ders o çekirdeğin yalnız sarmalama diliminde kalıyor.

#### Sayaç, Çağrı Yerine Hiç Dokunmadan Arttı

`cagrinin sarmalanmasi` satırı `açıklama` çıkıyor: `oncekiSarma` değişkeni `servis.rapor()`
çağrılmadan hemen önceki sayaç değerini tutuyor, sonra `servis.rapor()` çağrılıyor ve
sonrasında `kap.sarmaCagrisi > oncekiSarma` doğru çıkıyor — sayaç gerçekten arttı. Bu satırın
kendisinde `depo.oku()`'yu değil `servis.rapor()`'u çağırdığımıza dikkat etmek gerek:
`Servis.rapor()` içeride `depo.oku()`'ya gidiyor ve `depo` alanı aslında bir vekil olduğu
için bu iç çağrı vekilin `invoke` işleyicisinden geçiyor. `Servis`'in kaynak kodunda vekili
gösteren tek bir sözcük yok; `Depo depo` alanı yalnız arayüz tipini taşıyor, hangi somut
sınıfın (gerçek `BellekDepo` mu, onu saran vekil mi) arkasında durduğunu söylemiyor.

`sarmaCagrisi` bir örnek alanı, `Kap` sınıfının kendisi değil — bu ölçüm tek bir `kap`
nesnesi kullandığı için tek bir sayaç görüyoruz, ama iki ayrı `Kap` örneği aynı `BellekDepo`
sınıfını ayrı ayrı sarmalasaydı, ikisinin sayaçları da birbirinden bağımsız kalırdı. Sayaç
kabın kendi durumunun bir parçası, sarmalanan sınıfın değil — bu da `Servis`'in neden hiçbir
zaman kendi sayacını tutmadığını, sayacın her zaman kapta durduğunu açıklıyor.

Elle kurulan `BellekDepo` bu sarmalamayı hiç almıyor: `Proxy.isProxyClass(elle.getClass())`
`false` dönüyor, bu yüzden `elle kurulan nesnede sarmalama` satırı `varsayilan` çıkıyor —
kabın hiç karışmadığı bir durum. Birinci ders bunu önceden not etmişti; burada nedeni tam
görünür oluyor: sarmalama yalnız `al` yönteminin içinde, nesne kurulduktan hemen sonra
uygulanıyor. `new BellekDepo()` bu yöntemin dışında kaldığı için vekil hiç devreye girmiyor
ve elle kurulan nesne kalıcı olarak sarmalanmamış kalıyor — sonradan bir vekile
"dönüştürülemiyor", çünkü Java'da bir nesnenin kimliği kurulduktan sonra değişmiyor.

#### Sınırlayıcı Ölçüm: Görünen Ama Çalışmayan İşaret

`ArayuzsuzBilesen` sınıfı `@Kap.Bilesen @Kap.Sarmala` taşıyor — tıpkı `BellekDepo` gibi. Ama
bir farkla: `ArayuzsuzBilesen` hiçbir arayüz gerçekleştirmiyor, yalnız kendi başına bir
sınıf. `al` yöntemindeki sarmalama denetimi iki koşulu birden arıyor: `tip.
isAnnotationPresent(Sarmala.class) && tip.getInterfaces().length == 1`. `ArayuzsuzBilesen`
için birincisi doğru ama ikincisi yanlış — `getInterfaces().length` sıfır. Denetim
`false` sonucuna varıyor ve sarmalama bloğuna hiç girilmiyor; nesne çıplak hâliyle
döndürülüyor.

```
Object arayuzsuz = kap.al(ArayuzsuzBilesen.class);
Kap.kural("sarmalama arayuzsuz sinifa konuldu", arayuzsuz instanceof Proxy ? "hata" : "etkisiz");
```

`arayuzsuz instanceof Proxy` `false` çıkıyor, kural satırı `"etkisiz"` sınıfına düşüyor. Hiç
istisna yok — `Proxy.newProxyInstance` hiç çağrılmadığı için fırlatacak bir hata da yok.
Sonuç: `ArayuzsuzBilesen` kuruluyor, kullanılabiliyor, hiçbir şekilde bozuk görünmüyor — ama
üzerindeki `@Kap.Sarmala` işareti tamamen **etkisiz**. Bu, ortak modelin kör noktası: bir
sınıfın üzerinde duran bir açıklama, o açıklamanın gerçekten uygulandığı anlamına gelmiyor.
Kaynağı okuyan biri `@Kap.Sarmala`'yı görüp "bu sınıfın her çağrısı sayılıyor" diye
düşünebilir, ama bu doğru değil — `Proxy` mekanizması yalnız arayüz üzerinden çalışabiliyor,
çünkü Java'nın dinamik vekil düzeneği yalnız arayüzleri sarabiliyor, somut sınıfları değil.

Bu kusurun `etkisiz` sınıfında kalmasının nedeni tam bu teknik sınır: kap `ArayuzsuzBilesen`'i
sarmalamayı **reddetmiyor**, yalnız sarmalayamıyor ve bunu hiç söylemiyor. Dört kusur ailesi
içinde bu, en sessiz olanı — `hata` bağırıyor, `sessiz` yanlış bir değer üretiyor, ama
`etkisiz` hiçbir iz bırakmıyor; program beklenen gibi çalışıyor görünüyor, yalnız beklenen
tek bir davranış (sayaç artışı) hiç gerçekleşmiyor.

CK27'nin işaret ettiği gibi bu ölçüm yalnız **sıfır** arayüzlü uç durumu koşturuyor;
simetriğini — **birden çok** arayüz gerçekleştiren bir sınıfı — hiç denemiyor. Ama denetimin
kendi kod satırı (`tip.getInterfaces().length == 1`) bu durumda ne olacağını önceden
söylüyor: iki arayüz gerçekleştiren bir sınıf da tıpkı `ArayuzsuzBilesen` gibi denetimden
`false` alır ve sarmalanmadan döner — nedeni farklı (arayüz eksikliği değil, arayüz
fazlalığı) ama sonuç aynı, sessiz bir etkisizlik. Bu, tek bir koşul satırının iki ayrı uç
durumu aynı kusur sınıfına düşürmesinin bir örneği: `Proxy.newProxyInstance`'ın kendisi
birden çok arayüzü aynı vekilin arkasında toplayabiliyor — üçüncü bağımsız değişkeni bir
`Class<?>[]` dizisi kabul ediyor — ama bu ölçümün kendi denetimi bu yeteneği hiç
kullanmıyor, yalnız **tam bir** arayüz durumunu doğru işliyor. Kaynağı okuyan biri
`@Kap.Sarmala`'yı gördüğünde sınıfın kaç arayüz taşıdığına bakmadan "bu sınıf sarmalanacak"
diye düşünebilir; oysa doğru yanıt yalnız `getInterfaces().length`'in tam olarak 1 olup
olmadığına bağlı.

#### Altı Ders, Aynı Soru

Bu konunun altı dersi hep aynı soruyu sordu — bu davranışı kaynakta bir çağrı mı, bir
açıklama mı doğurdu, yoksa hiçbiri mi — ve her seferinde farklı bir köşeyi ölçtü. Birinci
ders bu soruyu `Kap.java`'nın tam çekirdeğiyle yanıtladı: nesnenin kurulmasını ve tekil
kalmasını, yapıcı üzerinden enjeksiyonu, kapsam ile kurulum yöntemini, ayar kaynaklarını ve
sarmalamayı tek bir koşumda ölçüp on iki davranışla dört kusuru
`3 çağrı, 6 açıklama, 3 varsayılan` ile `1 hata, 2 sessiz, 1 etkisiz` toplamına vardırdı.
İkinci, üçüncü ve bu ders aynı çekirdeğin yalnız kendi sorusuna değen dilimini yeniden kurup
koşturdu: yapıcı üzerinden enjeksiyonu ve bağlanmamış bir arayüzün hatasını, kapsam ile
kurulum yöntemini ve tanınmayan bir kapsam adının sessizliğini, şimdi de sarmalamayı ve
etkisiz kalan bir işareti — üçü de birinci dersin toplamının birer alt kümesi, kendi
başlarına yeniden aynı on iki/dört toplamına varmayı hiç iddia etmiyor. Dördüncü ve beşinci
ders bu çekirdeğin dışına çıkıp kendi ölçümlerini kurdu — üç yapılandırma kaynağının öncelik
sırası, koşullu otomatik varsayılanlar — ama ikisi de aynı sözlüğü sürdürdü: bir davranışın
kaynakta nereden doğduğunu sormaya ve bozulduğunda gürültülü mü yoksa sessiz mi kaldığını
ayırt etmeye devam etti.

#### Özet

- Bir sarmalama açıklaması, çağrı yerine hiç dokunmadan, çağrı sırasında araya giren bir
  vekil kurarak davranış ekliyor; `Servis`'in kaynak kodu hiç değişmiyor.
- Sarmalanan davranış (bu ölçümde bir sayaç) kaynakta hiçbir çağrı olarak görünmüyor —
  yalnız sayacın kendisi arttığında, koşturarak doğrulanabiliyor.
- Elle kurulan bir nesne kabın `al` yönteminden hiç geçmediği için sarmalanmıyor ve kalıcı
  olarak sarmalanmamış kalıyor.
- Arayüzü olmayan bir sınıfa konan sarmalama işareti hiçbir hata vermeden etkisiz kalıyor —
  bir açıklamanın var olması onun uygulanabilir olduğu anlamına gelmiyor.
- Birinci derste ölçülen on iki davranışın üçü kaynaktaki bir çağrıdan, altısı bir
  açıklamadan, üçü kabın kendi varsayılanından geliyordu; bu derste eklenen sarmalama ve
  etkisiz kalan işaret, o toplamın üzerine oturan iki yeni gözlem.

#### Sonraki Adım

Bu konunun altı dersi tek bir nesne kabının kendi iç işleyişini ölçtü: nesnenin kurulması,
bağımlılığın sağlanması, kapsam ve yaşam döngüsü, ayarların kaynağı, koşullu varsayılanlar
ve şimdi çağrı yerine hiç dokunmadan eklenen davranış. Bu son ders bir sınır da çizdi: bir
vekil yalnız arayüzün **ulaşabildiği** yere kadar davranış ekleyebiliyor, ötesine geçemiyor.
Sıradaki konu aynı kabı, kendi iç işleyişinin dışında, **bir isteği karşılarken** izliyor —
uç noktalar, doğrulama ve veri erişimi de tam bu konuda ölçülen aynı üç kaynaktan doğuyor:
bir çağrıdan, bir açıklamadan, ya da kabın kendi varsayılanından.

## Web ve Veri Katmanı

Gömülü bir sunucunun istek işleme zincirini kurması, uç noktaların ve yönlendirmenin açıklamalarla bildirilmesi, doğrulama ile ortak hata yanıtının çağrı yerinde görünmeyen üretimi, veri erişiminin arayüz düzeyinde soyutlanması, nesne ile tablo arasındaki bağlamanın yaşam döngüsü ve tembel yükleme sonuçları, işlem sınırlarının bildirimle çizilmesi.

### Gömülü Sunucu ve İstek İşleme Modeli

Source: https://academia.sh/tr/kurslar/kurumsal-uygulama-cercevesi/gomulu-sunucu-ve-istek-isleme-modeli

İstek işleme zincirinin halkaları üç kaynağa ayrılır: nesne kabının kendi varsayılanı, bir açıklama ve açık bir çağrı. Aynı halka kümesi farklı sırayla bildirildiğinde kabın ürettiği çalışma sırası değişmiyor; açıklama taşıyan bir sınıf kabın dışında elle çağrıldığında ise açıklamanın hiçbir etkisi kalmıyor.

Önceki konunun son dersi çağrı yerine hiç dokunmadan davranış eklenebildiğini, bunun sınırının
da vekilin ulaşabildiği yer olduğunu gösterdi. Bu konu aynı kabı bir soruyla izliyor: kap bir
isteği karşılarken aynı üç kaynak — çağrı, açıklama, varsayılan — hangi oranda devrede? Uç
noktalar, doğrulama ve veri erişimi de aynı üç kaynaktan doğuyor; bu ders zincirin en dışındaki
katmandan başlıyor ve isteğin kaba girişinden çıkışına kadar hangi halkadan geçtiğini sayıyor.

Sunucu burada **modellenir**. Gerçek bir soket açılmaz, gerçek bir bağlantı noktası dinlenmez,
ağdan gerçek bir bayt okunmaz — bir isteğin kaptan geçerken izlediği yol, bir dizgenin üzerinden
sırayla geçen küçük işlev nesneleri olarak kurulur. Bu basitleştirme dersin sorusunu
değiştirmiyor: gerçek bir gömülü sunucuda da istek, dinleyici soketten uygulama koduna ulaşana
kadar sabit bir **halka** dizisinden geçer ve bu dizinin kaç halkasının kaynakta bir karşılığı
olduğu sorusu soket gerçek olsun ya da olmasın aynı kalır. Sunucunun kendisinin görevi, bağlantıyı
kabul etmesi ve isteği bu zincire sokması — **M16/K01 Sunucu Tarafı Temelleri** kursu bunu ve
zincirin genel fikrini kavram olarak kurdu, burada tekrarlanmıyor. Kursun kendi terimi **istek
işleme zinciri**, aynı kursun **ara katman zinciri** teriminden ayrı tutulur: ara katman zinciri
çağıranın elle dizdiği bir sıradır, istek işleme zinciri ise bu derste görüleceği gibi kabın kendi
kuralına göre kurduğu bir sıradır.

Sunucunun modellenmesinin bir başka nedeni ölçümün kendisiyle ilgili: gerçek bir bağlantı
noktası açılsaydı, hangi bağlantı noktasının boşta olduğu makineye göre değişir ve bu ders
serisinin bütününde geçerli olan ortama bağlı veri yasağı böyle bir değeri yazmayı zaten
engeller. Dizgeyle kurulan model bu sorunu baştan kaldırıyor — aynı programın iki ayrı
makinede iki ayrı çıktı verme ihtimali sıfıra iniyor, çünkü ölçülen şey ağdaki bir gecikme ya
da bir bağlantı noktası numarası değil, kabın sabit bir sırayla uyguladığı işlev zinciri.
Gerçek bir gömülü sunucuda bu zincirin en dış halkası ağdan okuma ve ağa yazmadır; burada o
halka atlanıyor ve zincir doğrudan uygulama kodunun gördüğü ilk noktadan, isteğin kendisinden
başlıyor.

#### Halkaların Üç Kaynağı

- **WV1.** İstek burada bir dizge (`govde`) olarak modellenir; her halka bu dizgeye kendi
  imzasını ekler; ağdan okuma ve ağa yazma modellenmez.
- **WV2.** Açıklamalı sınıfların keşfi gerçek bir sınıf yolu taraması yapmaz; kap, `tara`
  çağrısına verilen aday sınıf listesini gözden geçirir. Bu, gerçek bir çerçevenin sınıf
  yolunu dolaşan taramasının modellenmiş bir karşılığıdır.

Zincirin bir halkası üç yoldan biriyle var olabiliyor. Kabın kendi gövdesine yazılmış, hiçbir
sınıfa hiçbir açıklama koymadan hep çalışan bir halka **varsayılandır** — burada isteğe bir
kimlik ekleyen ve zincirin sonunda hataları yakalayan iki halka bu gruba giriyor, ikisi de
`Kap` sınıfının kendi `calistir` yönteminin içinde yazılı ve hiçbir dış çağrıya ihtiyaç
duymuyor. Bir sınıfın üzerine konan `@Halka` açıklaması, kap o sınıfı tanıdığında zincire
eklenmesini **istiyor**; bu istek kabın `tara` yöntemine bir aday listesi verilmeden hiçbir
şey yapmıyor. Üçüncü yol ise en çıplak olanı: kodun kendisi `kap.ekle(...)` çağrısıyla bir
halkayı doğrudan zincire koyuyor — kaynakta bunu doğuran satır tam olarak bu satır.

```java
// Kap.java — istek isleme zincirinin halkalarini kim ekledi: cagiran mi, aciklama mi, kabin varsayilani mi
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.util.*;

public class Kap {

    @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE)
    @interface Halka { int oncelik() default 100; }

    interface HalkaIslevi { String uygula(String govde, List<String> iz); }

    final List<Class<?>> aciklamaliSinif = new ArrayList<>();
    final List<HalkaIslevi> cagriliHalka = new ArrayList<>();
    final List<String> cagriliAd = new ArrayList<>();

    void tara(Class<?>... adaylar) {
        for (Class<?> aday : adaylar)
            if (aday.isAnnotationPresent(Halka.class)) aciklamaliSinif.add(aday);
    }

    void ekle(String ad, HalkaIslevi h) {
        cagriliAd.add(ad);
        cagriliHalka.add(h);
    }

    String calistir(String govde, List<String> iz) {
        try {
            String s = govde;
            iz.add("istek-kimligi(varsayilan)");

            List<Class<?>> siraliAciklamali = new ArrayList<>(aciklamaliSinif);
            siraliAciklamali.sort(Comparator.comparingInt(c -> c.getAnnotation(Halka.class).oncelik()));
            for (Class<?> c : siraliAciklamali) {
                HalkaIslevi h = (HalkaIslevi) c.getDeclaredConstructor().newInstance();
                iz.add(c.getSimpleName() + "(aciklama)");
                s = h.uygula(s, iz);
            }
            for (int i = 0; i < cagriliHalka.size(); i++) {
                iz.add(cagriliAd.get(i) + "(cagri)");
                s = cagriliHalka.get(i).uygula(s, iz);
            }
            iz.add("hata-yakalama(varsayilan)");
            return s;
        } catch (ReflectiveOperationException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        Kap kap1 = new Kap();
        kap1.tara(GunlukHalkasi.class, SikistirmaHalkasi.class);
        kap1.ekle("baslik-ekleme", (g, iz) -> g + "+baslik");
        kap1.ekle("uzunluk-yazma", (g, iz) -> g + "+uzunluk");
        List<String> iz1 = new ArrayList<>();
        String sonuc1 = kap1.calistir("istek", iz1);

        Kap kap2 = new Kap();
        kap2.tara(SikistirmaHalkasi.class, GunlukHalkasi.class);
        kap2.ekle("baslik-ekleme", (g, iz) -> g + "+baslik");
        kap2.ekle("uzunluk-yazma", (g, iz) -> g + "+uzunluk");
        List<String> iz2 = new ArrayList<>();
        String sonuc2 = kap2.calistir("istek", iz2);

        Kap kap3 = new Kap();
        kap3.tara(GunlukHalkasi.class, SikistirmaHalkasi.class);
        kap3.ekle("uzunluk-yazma", (g, iz) -> g + "+uzunluk");
        kap3.ekle("baslik-ekleme", (g, iz) -> g + "+baslik");
        List<String> iz3 = new ArrayList<>();
        String sonuc3 = kap3.calistir("istek", iz3);

        System.out.println("-- kurulum-1: G,S tarandi; baslik sonra uzunluk eklendi --");
        iz1.forEach(x -> System.out.println("  " + x));
        System.out.println("-- kurulum-2: S,G tarandi (aday sirasi ters); baslik sonra uzunluk eklendi --");
        iz2.forEach(x -> System.out.println("  " + x));
        System.out.println("-- kurulum-3: G,S tarandi; uzunluk sonra baslik eklendi (cagri sirasi ters) --");
        iz3.forEach(x -> System.out.println("  " + x));

        System.out.println("kurulum-1 ile kurulum-2 sirasi ayni mi (aciklamali kisim): " + iz1.equals(iz2));
        System.out.println("kurulum-1 ile kurulum-3 sirasi ayni mi (cagrili kisim de dahil): " + iz1.equals(iz3));
        System.out.println("govde sonuclari ayni mi (1 ve 2): " + sonuc1.equals(sonuc2));

        int varsayilan = 0, aciklama = 0, cagri = 0;
        for (String satir : iz1) {
            if (satir.contains("(varsayilan)")) varsayilan++;
            else if (satir.contains("(aciklama)")) aciklama++;
            else if (satir.contains("(cagri)")) cagri++;
        }
        System.out.printf("zincir: %d varsayilan, %d aciklama, %d cagri, toplam %d halka%n",
                varsayilan, aciklama, cagri, varsayilan + aciklama + cagri);

        System.out.println("-- elle kurulan zincir: kap hic devrede degil --");
        List<String> izElle = new ArrayList<>();
        SikistirmaHalkasi s1 = new SikistirmaHalkasi();
        GunlukHalkasi g1 = new GunlukHalkasi();
        String govdeElle = "istek";
        izElle.add("SikistirmaHalkasi(elle)");
        govdeElle = s1.uygula(govdeElle, izElle);
        izElle.add("GunlukHalkasi(elle)");
        govdeElle = g1.uygula(govdeElle, izElle);
        izElle.forEach(x -> System.out.println("  " + x));
        System.out.println("elle kurulumda varsayilan halka sayisi: "
                + izElle.stream().filter(x -> x.contains("varsayilan")).count());
        System.out.println("elle kurulumda ilk cagrilan sinif Sikistirma mi (kap'ta oncelik 20, Gunluk'ten sonra gelmesi gerekirdi): "
                + izElle.get(0).startsWith("Sikistirma"));
        System.out.println("kusur turu: etkisiz (istisna yok, @Halka.oncelik degeri hic okunmadi, sira degismedi)");
    }
}

@Kap.Halka(oncelik = 10)
class GunlukHalkasi implements Kap.HalkaIslevi {
    public String uygula(String govde, List<String> iz) { return govde + "+gunluk"; }
}

@Kap.Halka(oncelik = 20)
class SikistirmaHalkasi implements Kap.HalkaIslevi {
    public String uygula(String govde, List<String> iz) { return govde + "+sikistirma"; }
}
```

```
-- kurulum-1: G,S tarandi; baslik sonra uzunluk eklendi --
  istek-kimligi(varsayilan)
  GunlukHalkasi(aciklama)
  SikistirmaHalkasi(aciklama)
  baslik-ekleme(cagri)
  uzunluk-yazma(cagri)
  hata-yakalama(varsayilan)
-- kurulum-2: S,G tarandi (aday sirasi ters); baslik sonra uzunluk eklendi --
  istek-kimligi(varsayilan)
  GunlukHalkasi(aciklama)
  SikistirmaHalkasi(aciklama)
  baslik-ekleme(cagri)
  uzunluk-yazma(cagri)
  hata-yakalama(varsayilan)
-- kurulum-3: G,S tarandi; uzunluk sonra baslik eklendi (cagri sirasi ters) --
  istek-kimligi(varsayilan)
  GunlukHalkasi(aciklama)
  SikistirmaHalkasi(aciklama)
  uzunluk-yazma(cagri)
  baslik-ekleme(cagri)
  hata-yakalama(varsayilan)
kurulum-1 ile kurulum-2 sirasi ayni mi (aciklamali kisim): true
kurulum-1 ile kurulum-3 sirasi ayni mi (cagrili kisim de dahil): false
govde sonuclari ayni mi (1 ve 2): true
zincir: 2 varsayilan, 2 aciklama, 2 cagri, toplam 6 halka
-- elle kurulan zincir: kap hic devrede degil --
  SikistirmaHalkasi(elle)
  GunlukHalkasi(elle)
elle kurulumda varsayilan halka sayisi: 0
elle kurulumda ilk cagrilan sinif Sikistirma mi (kap'ta oncelik 20, Gunluk'ten sonra gelmesi gerekirdi): true
kusur turu: etkisiz (istisna yok, @Halka.oncelik degeri hic okunmadi, sira degismedi)
```

Toplam altı halka üç kaynağa bölünüyor: `istek-kimligi` ve `hata-yakalama` **varsayılan**dır —
`Kap` sınıfının hiçbir dış girdiye ihtiyaç duymadan kendi gövdesine yazdığı davranış.
`GunlukHalkasi` ve `SikistirmaHalkasi` **açıklama**dır — kaynakta bu iki sınıfı çağıran hiçbir
satır yok, yalnız üzerlerinde bir `@Halka` işareti var ve o işareti okuyan `tara` çağrısı var.
`baslik-ekleme` ile `uzunluk-yazma` ise **çağrı**dır — `kap.ekle(...)` satırı olmadan bu iki halka
zincirde hiç bulunmaz. Altı halka üç kaynağa eşit bölünüyor — ikişer ikişer — ama bu eşitlik bir
kural değil, bu dersin seçtiği örneğin bir özelliği; ortak tanımın on iki davranışı çağrı,
açıklama ve varsayılan arasında eşit bölünmüyordu; açıklama ağır basıyordu. Kursun sorduğu soru
burada da aynı: bu kez ölçülen şey bir bileşenin kuruluşu değil, bir isteğin bir kaptan geçerken
izlediği yol.

Kaynak metni yukarıdan aşağı okuyan biri bu altı halkanın yalnızca ikisini doğrudan görüyor.
`kap.ekle("baslik-ekleme", ...)` ve `kap.ekle("uzunluk-yazma", ...)` satırları oradadır ve
bir okuyucu bu iki halkanın var olduğunu ve hangi sırayla eklendiğini kaynaktan çıkarabilir.
Geri kalan dördü kaynağın başka bir yerinde iz bırakıyor: `GunlukHalkasi` ile
`SikistirmaHalkasi` için tek iz sınıf tanımının üzerindeki `@Halka` satırı, `istek-kimligi`
ile `hata-yakalama` için ise hiçbir iz yok — bu ikisi yalnız `Kap.calistir` yönteminin
gövdesini okuyarak bulunabiliyor ve o yöntem çağıran kodun hiç görmediği bir dosyada
duruyor. Kaynağı yalnız çağıran tarafın dosyasından okuyan biri, zincirin üçte ikisinin
nereden geldiğini kaçırıyor.

#### Kap Kendi Sırasını Uygular

- **WV3.** `oncelik` değeri küçük olan açıklamalı halka önce çalışır; bu, kabın kendi
  kuralıdır ve kaynakta hiçbir yerde yazılı değildir.

Kurulum-1 ile kurulum-2 arasındaki tek fark `tara` çağrısına verilen sınıfların sırası:
birinde önce günlük, sonra sıkıştırma sınıfı taranıyor, ötekinde tam tersi. Kaynak metni
farklı yazıldığı hâlde iz listesi birebir aynı çıkıyor, çünkü kap taranan sınıfları kendi
`oncelik` alanına göre yeniden sıralıyor ve `tara` çağrısına verilen dizinin sırasını hiç
kullanmıyor. **Halkaların çalışma sırası çağıranın kaynakta yazdığı sıra değildir; kap kendi
sırasını uygular ve iki farklı yazım aynı sırayı üretir.**

Ama bu kural yalnız açıklamayla bulunan halkalar için geçerli. Kurulum-3, kurulum-1'le aynı
tarama sırasını kullanıyor, farkı yalnız iki `ekle` çağrısının sırası: önce uzunluk, sonra
başlık. İz listesi bu kez farklı çıkıyor — `uzunluk-yazma` önce, `baslik-ekleme` sonra
görünüyor. Kap, açıklamayla bulduğu halkalar için kendi sıralama kuralını uyguluyor, ama açık
bir `ekle` çağrısıyla eklenen halkalar için hiçbir sıralama yapmıyor; onları tam olarak
çağrıldıkları sırada zincire koyuyor. Sıra kuralı yalnız kabın **keşfettiği** şeye uygulanıyor,
kabın **söylenildiği** şeye değil — bir açıklama kabın kendi kararına bırakılmış bir istektir,
bir çağrı ise çağıranın kendi kararıdır ve kap onu değiştirmiyor.

Bu ayrımın pratik bir sonucu var: iki geliştirici aynı iki açıklamalı sınıfı farklı bir
sırayla `tara`'ya verse — biri dosyaları alfabetik, öteki değişiklik tarihine göre listeleyen
bir araçla topladıysa — kaynak metinleri farklı görünür ama üretilen zincir birebir aynı
kalır. Sıra kaynakta değil, `oncelik` alanında yaşıyor; kod incelemesi yapan biri iki
sürümün de aynı davranışı ürettiğini görmek için `tara` çağrısındaki sıraya değil, açıklama
tiplerinin kendi tanımına bakmak zorunda. Açık çağrı için bu güvence yok — `ekle` çağrılarının
sırasını değiştirmek zincirin sırasını da değiştiriyor, çünkü kap orada hiçbir kural
uygulamıyor.

#### Elle Kurulan Zincirde Açıklamanın Hiçbir Etkisi Yok

- **WV4.** Elle kurulan çağrıda `SikistirmaHalkasi` ile `GunlukHalkasi` doğrudan
  örnekleniyor ve `uygula` yöntemleri sırasıyla çağrılıyor; hiçbir `Kap` nesnesi
  oluşturulmuyor.
- **WV5.** `@Halka` açıklaması iki sınıfın üzerinde durmaya devam ediyor; kaldırılmadı,
  silinmedi. Elle kurulumda etkisiz kalmasının nedeni açıklamanın kaybolması değil, onu
  okuyan bir tarafın olmaması.

Son ölçüm kabı tamamen devre dışı bırakıyor: `SikistirmaHalkasi` ile `GunlukHalkasi` `new`
ile elle kuruluyor, `uygula` yöntemleri elle, sırayla çağrılıyor. İki sonuç dikkat çekiyor.
Birincisi, iz listesinde `varsayilan` etiketli hiçbir satır yok — `istek-kimligi` ve
`hata-yakalama` `Kap.calistir` yönteminin gövdesine yazılı ve bu yöntem hiç çağrılmadığı için
o iki davranış da hiç gerçekleşmiyor. İkincisi, `SikistirmaHalkasi` önce çağrılıyor,
`GunlukHalkasi` sonra — oysa kap içinde bu ikisinin sırası tam tersiydi, çünkü
`SikistirmaHalkasi`'nin `oncelik`i 20, `GunlukHalkasi`'ninki 10. Elle çağrıda bu sayı hiç
okunmuyor; `@Halka(oncelik = 20)` açıklaması sınıfın üzerinde duruyor ama onu okuyacak bir
`tara` çağrısı, bir sıralama adımı yok. Açıklama bir özellik değil bir istektir; isteği yerine
getirecek bir kap yoksa iki sınıf da sıradan iki Java nesnesinden farksız çağrılıyor ve sıra
tamamen çağıranın elinde kalıyor.

Bu, dersin başındaki üç kaynak ayrımını tamamlıyor. Varsayılan davranış kabın kendi kodunun
parçası olduğu için kap yoksa hiç yok; açıklama bir isteği taşıdığı için okuyan kap yoksa
etkisiz; yalnız çağrı — bir yöntemin doğrudan çağrılması — kap olsun olmasın aynı davranışı
üretiyor, çünkü çağrı zaten kaynakta yazılı bir ifadedir. Bu üç davranışın kap içindeki ve kap
dışındaki farkı, kabın asıl kattığı şeyin ne olduğunu gösteriyor: kap yeni bir yetenek
eklemiyor, kaynakta yazılmayan iki türden isteği (varsayılan ve açıklama) okuyup uyguluyor.

Bu sonucun sınayıcı için pratik bir uyarısı var: `GunlukHalkasi` ya da `SikistirmaHalkasi`
sınıfını kap dışında bir birim testinde çağırmak, o sınıfın kendi mantığını doğrular ama
zincirdeki yerini, sırasını ya da onunla birlikte çalışan varsayılan halkaları hiç sınamaz. İki
ölçüm ayrı sorulara yanıt veriyor: sınıfın kendi davranışı doğru mu ve kap onu doğru yere mi
koyuyor. Birinci soru elle kurulan nesneyle yanıtlanabilir; ikinci soru yalnız kabın kendisi
çalıştırılarak yanıtlanabilir — bu derste kurulum-1, kurulum-2 ve kurulum-3'ün birlikte yaptığı da
tam olarak bu.

#### Özet

- İstek işleme zincirinin altı halkası üç kaynağa ayrıldı: ikisi kabın varsayılanı, ikisi bir
  açıklama, ikisi açık bir çağrı.
- Açıklamayla bulunan halkaların çalışma sırası, kabın kendi `oncelik` kuralına göre
  belirleniyor ve kaynaktaki tarama sırasından bağımsız; iki farklı tarama sırası aynı
  sonucu üretti.
- Açık çağrıyla eklenen halkaların sırası kabın kuralına girmiyor; çağıranın yazdığı sıra
  aynen korunuyor.
- Kap devre dışı bırakılıp aynı sınıflar elle çağrıldığında varsayılan halkalar hiç
  çalışmadı ve açıklamadaki `oncelik` değeri hiç okunmadı; açıklama okuyan bir taraf
  olmadan etkisizdi.
- İki kurulumun ürettiği govde sonucu, sıra aynı kaldığı sürece birebir aynı çıktı.

#### Sonraki Adım

Bu ders bir isteğin kaptan geçerken izlediği zinciri ölçtü, ama zincirin sonunda isteğin
hangi işi karşıladığı hiç belirlenmedi — halkalar isteği yalnız değiştirdi, yönlendirmedi.
Sıradaki ders bu boşluğu kapatıyor: bir uç noktanın kaynakta hangi ifadeyle değil hangi
açıklamayla bildirildiğini ve eşleşmeyen bir isteğe kabın ne yanıt verdiğini ölçüyor.

### Denetleyiciler ve Yönlendirme

Source: https://academia.sh/tr/kurslar/kurumsal-uygulama-cercevesi/denetleyiciler-ve-yonlendirme

Dört uç noktanın dördü de kaynakta hiçbir çağrı olmadan yalnız bir açıklamayla bildiriliyor. Eşleşmeyen bir yol istendiğinde nesne kabı hata vermiyor, sabit bir varsayılan yanıt döndürüyor; aynı yolu bildiren iki bileşen tarandığında ise kazanan tarama sırasına bağlı kalıyor.

Önceki ders bir isteğin kaptan geçerken hangi halkalardan geçtiğini ölçtü, ama halkaların sonunda
isteğin hangi işi karşıladığı hiç belirlenmedi. Bu ders o boşluğu kapatıyor: bir uç noktanın
kaynakta hangi çağrıyla değil hangi açıklamayla bildirildiğini ve eşleşmeyen bir isteğe kabın ne
yanıt verdiğini ölçüyor. Kaynak tasarımı, bir yolun hangi kaynağı ve hangi eylemi temsil ettiği
**M16/K02 Web API Tasarımı** kursunda kavram olarak kuruldu ve burada tekrarlanmıyor; bu dersin
sorduğu soru ondan ayrı — bir yolun kaynakta **nasıl var olduğu**.

Denetleyici burada da modellenir: gerçek bir HTTP yöntemi ayrıştırılmaz; gerçek bir istek gövdesi
çözülmez. Bir "yol" yalnız bir dizgedir (`"/urun/liste"` gibi) ve bir uç nokta bu dizgeyi bir Java
yöntemine bağlayan bir eşleştirmedir. Önceki dersteki halkalar isteği değiştiriyordu,
yönlendirmiyordu; bu dersin denetleyicileri ise isteği hiç değiştirmeden doğru yönteme
**yönlendiriyor** — kursun terim kararında `routing` karşılığı budur.

Bir **denetleyici** burada sıradan bir Java sınıfıdır; onu denetleyici yapan tek şey,
yöntemlerinden bazılarının üzerinde `@UcNokta` açıklamasının bulunmasıdır. Sınıfın kendisi
hiçbir arayüz uygulamıyor, hiçbir üst sınıftan türemiyor — kabın onu "denetleyici" olarak
tanıması, sınıfın tipinden değil, `tara` çağrısına elle verilmesinden geliyor. Bu, kursun
web katmanındaki ikinci modelleme kararı: gerçek çerçevelerde sınıf taraması genellikle
sınıf yolunun tamamını dolaşır, burada ise `tara`'ya hangi nesnelerin denetleyici olarak
değerlendirileceği açıkça bir dizi olarak veriliyor. Modelleme farkı sonucu değiştirmiyor —
her iki durumda da kaynakta denetleyici sınıfını **çağıran** bir satır yok, yalnız üzerinde
duran bir açıklama var.

#### Dört Yolun Dördü de Açıklamadan Doğuyor

- **WV6.** `@UcNokta` yalnız bir yöntemin üzerine konur ve tek bir alan taşır: `yol`. Bu
  açıklamayı okuyan taraf, aşağıdaki `tara` yöntemidir.
- **WV7.** Yönlendirme tablosu bir `Map<String, Method>` olarak tutulur; anahtar yol
  dizgesi, değer yansımayla çağrılacak yöntemdir. Gerçek bir istek gövdesi ayrıştırma ya
  da içerik türü pazarlığı burada yoktur.

İki denetleyici sınıfı, dördü de `@UcNokta` taşıyan dört yöntem tanımlıyor. Hiçbir yerde
`kap.ekle("/urun/liste", ...)` biçiminde bir çağrı yok — önceki dersin çağrı ile eklenen
halkalarından farklı olarak, burada yönlendirme tablosunun tamamı `tara` çağrısının sınıfların
üzerindeki açıklamaları okumasıyla kuruluyor. Bu, önceki dersin karışık kaynak dağılımından
(iki varsayılan, iki açıklama, iki çağrı) ayrılan bir uç: dört uç noktanın **dördü de**
açıklamadan doğuyor, ne bir varsayılan uç nokta var ne de elle eklenen bir çağrı.

```java
// Kap.java — uc noktalar aciklamadan doguyor, cakisma kayit sirasina bagli
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.util.*;

public class Kap {

    @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD)
    @interface UcNokta { String yol(); }

    final Map<String, Method> yollar = new LinkedHashMap<>();
    final Map<String, Object> sahibi = new LinkedHashMap<>();
    final List<String> kayitSirasi = new ArrayList<>();

    void tara(Object... denetleyiciler) {
        for (Object d : denetleyiciler) {
            for (Method m : d.getClass().getDeclaredMethods()) {
                UcNokta u = m.getAnnotation(UcNokta.class);
                if (u == null) continue;
                yollar.put(u.yol(), m);
                sahibi.put(u.yol(), d);
                kayitSirasi.add(u.yol() + " <- " + d.getClass().getSimpleName());
            }
        }
    }

    String istek(String yol) {
        Method m = yollar.get(yol);
        if (m == null) return "varsayilan-yanit: sayfa hazir degil";
        try {
            return (String) m.invoke(sahibi.get(yol));
        } catch (ReflectiveOperationException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) throws Exception {
        UrunDenetleyici urun = new UrunDenetleyici();
        StokDenetleyici stok = new StokDenetleyici();

        Kap kap = new Kap();
        kap.tara(urun, stok);

        String[] dortYol = {"/urun/liste", "/urun/ekle", "/stok/durum", "/stok/uyari"};
        System.out.println("-- dort yolun tamami aciklamadan dogdu --");
        int aciklamadan = 0;
        for (String yol : dortYol) {
            String yanit = kap.istek(yol);
            System.out.printf("%-16s%s%n", yol, yanit);
            if (kap.yollar.containsKey(yol)) aciklamadan++;
        }
        System.out.println("aciklamadan dogan yol sayisi: " + aciklamadan + "/4");

        System.out.println("-- eslesmeyen yol --");
        System.out.println("/urun/sil       " + kap.istek("/urun/sil"));
        System.out.println("kusur turu: sessiz (istisna yok, sabit bir varsayilan yanit dondu)");

        System.out.println("-- cakisan uc nokta: iki bilesen ayni yolu bildiriyor --");
        YedekDenetleyici yedek = new YedekDenetleyici();

        Kap kapA = new Kap();
        kapA.tara(urun, yedek);
        System.out.println("tarama sirasi urun-yedek, /urun/liste yaniti: " + kapA.istek("/urun/liste"));

        Kap kapB = new Kap();
        kapB.tara(yedek, urun);
        System.out.println("tarama sirasi yedek-urun, /urun/liste yaniti: " + kapB.istek("/urun/liste"));

        System.out.println("iki tarama sirasi ayni yaniti mi veriyor: "
                + kapA.istek("/urun/liste").equals(kapB.istek("/urun/liste")));

        System.out.println("-- elle cagrilan denetleyici: kap kaydindan bagimsiz --");
        System.out.println("urun.liste() dogrudan cagrildi: " + urun.liste());
        System.out.println("kapA uzerinden /urun/liste (son kayit yedek): " + kapA.istek("/urun/liste"));
        System.out.println("elle cagri ile kap yaniti ayni mi: " + urun.liste().equals(kapA.istek("/urun/liste")));
    }
}

class UrunDenetleyici {
    @Kap.UcNokta(yol = "/urun/liste")
    public String liste() { return "3 urun"; }

    @Kap.UcNokta(yol = "/urun/ekle")
    public String ekle() { return "urun eklendi"; }
}

class StokDenetleyici {
    @Kap.UcNokta(yol = "/stok/durum")
    public String durum() { return "stok: 42"; }

    @Kap.UcNokta(yol = "/stok/uyari")
    public String uyari() { return "uyari yok"; }
}

class YedekDenetleyici {
    @Kap.UcNokta(yol = "/urun/liste")
    public String liste() { return "yedek liste"; }
}
```

```
-- dort yolun tamami aciklamadan dogdu --
/urun/liste     3 urun
/urun/ekle      urun eklendi
/stok/durum     stok: 42
/stok/uyari     uyari yok
aciklamadan dogan yol sayisi: 4/4
-- eslesmeyen yol --
/urun/sil       varsayilan-yanit: sayfa hazir degil
kusur turu: sessiz (istisna yok, sabit bir varsayilan yanit dondu)
-- cakisan uc nokta: iki bilesen ayni yolu bildiriyor --
tarama sirasi urun-yedek, /urun/liste yaniti: yedek liste
tarama sirasi yedek-urun, /urun/liste yaniti: 3 urun
iki tarama sirasi ayni yaniti mi veriyor: false
-- elle cagrilan denetleyici: kap kaydindan bagimsiz --
urun.liste() dogrudan cagrildi: 3 urun
kapA uzerinden /urun/liste (son kayit yedek): yedek liste
elle cagri ile kap yaniti ayni mi: false
```

Dört uç noktanın dördü de `aciklamadan dogan yol sayisi: 4/4` satırında doğrulanıyor. Kaynağı
okuyan biri `UrunDenetleyici` ve `StokDenetleyici` sınıflarına bakıp dört `@UcNokta`
açıklaması görür, ama bu dört açıklamanın gerçekten bir yönlendirme tablosuna dönüştüğünü
kaynaktan **çıkaramaz** — dönüşümü yapan `tara` yöntemi çağıran kodun hiç görmediği bir yerde
duruyor. Bu, önceki dersle aynı okuma sorununu tekrar üretiyor: açıklama kaynakta bir işaret
bırakıyor, ama işaretin gerçekten bir davranışa dönüştüğü yalnız kabın kendi kodunu okuyarak
anlaşılıyor.

Bu noktada iki denetleyici sınıfının kendi başına hiçbir işlevi yok — `UrunDenetleyici` ile
`StokDenetleyici`, kaba hiç tanıtılmadan da derlenir, örneklenebilir, yöntemleri çağrılabilir.
Bir sınıfı "denetleyici" yapan şey onun kendi kodu değil, kabın onu tanıması. Bu, önceki
derste `@Halka` açıklamasında da görülen kalıbın burada tekrarı: açıklama sınıfın üzerinde
durduğu sürece bir istektir, isteği yerine getirecek bir okuyucu yoksa sınıf sıradan bir Java
sınıfından farksız kalır. Fark şu: bu derste isteği yerine getiren okuyucu,
sınıfı bir bileşen olarak kurmuyor, yalnız yöntemlerinden birini bir yol dizgesine bağlıyor.
Aynı açıklama düzeneği, konuya göre tamamen farklı bir sonuç üretebiliyor — kap ne
yapacağına, açıklamanın taşıdığı bilgiye ve kendi kuralına bakarak karar veriyor.

#### Eşleşmeyen Yol: Sessiz Bir Varsayılan

- **WV8.** `istek` yöntemi eşleşme bulamadığında sabit bir dizge döndürüyor, hiçbir istisna
  fırlatmıyor. Bu sabit dizge normal bir yanıtla aynı tipte (`String`) olduğu için çağıran
  taraf onu ayırt etmek zorunda kalmadan bir "yanıt" alıyor.

`/urun/sil` hiçbir denetleyicide tanımlı değil ve `kap.istek("/urun/sil")` çağrısı bir
istisna fırlatmak yerine `"varsayilan-yanit: sayfa hazir degil"` döndürüyor. Bu, kursun dört
kusur sınıfından **sessiz** olanının bir örneği: program çöküyor demiyor, bir hata mesajı
basmıyor, yalnız beklenmeyen bir yanıtla devam ediyor. Bir geliştirici `/urun/sill` gibi bir
yazım hatası yaparsa, kod hiçbir yerde patlamaz — yanıt gelir, biçimi doğrudur, ama içeriği
hiçbir zaman gerçek bir uç noktanın ürettiği içerik değildir. Kusuru yakalayan tek şey,
yanıtın **içeriğini** bilerek karşılaştırmaktır; yanıtın **var olması** hiçbir şey
kanıtlamıyor.

Bu davranış bir tasarım kararı, bir eksiklik değil — kap eşleşmeyen her yol için aynı sabit yanıtı
üretiyor ve bu tutarlı. Ama tutarlılık ile doğruluk aynı şey değil: eşleşmeyen bir yol her zaman
aynı yanıtı üretmesi, o yanıtın çağıranın beklediği şey olduğu anlamına gelmiyor. Denetimin Ters
Çevrilmesi dersinde kap eksik bir tip istendiğinde `IllegalStateException` fırlatıyordu — orada
kap hiç bilmediği bir sınıfı kurmaya çalışıyordu ve bu gerçekten yürütülemez bir durumdu. Burada
ise kap bir yol bulamadığında yürütülebilir bir durumdayken sessiz kalmayı seçiyor, çünkü "bu yol
yok" bilgisini bir istisna yerine bir yanıt gövdesi olarak taşımak da kabın verdiği bir karar.

Bu kararın alternatifi kaptan değil, yöntemin imzasından da görülebilir: `istek` bir `String`
yerine bir `Optional<String>` döndürseydi, çağıranın eşleşmeme durumunu kontrol etmesi
zorunlu hâle gelirdi — tip sistemi eşleşmeyen yolu ayrı bir durum olarak taşırdı. Burada
seçilen imza bunu yapmıyor; "bulundu" ile "bulunamadı" aynı tipte, aynı biçimde geri
dönüyor. Sınırlayıcı ölçüm burada tam bu noktaya oturuyor: kusurun sessiz kalması kabın bir
zayıflığı değil, dönüş tipinin eşleşme durumunu ayırt etmemesinin doğrudan sonucu. Bir
yönlendirme kuralı yazan kişi bu ayrımı bilerek tasarlamak zorunda; aksi hâlde her yazım
hatası, hatasız görünen ama yanlış bir yanıtla sonuçlanır.

#### Çakışan Uç Nokta: Kazanan Tarama Sırasına Bağlı

- **WV9.** `YedekDenetleyici`, `UrunDenetleyici` ile aynı yolu (`/urun/liste`) bildiriyor.
  Kap, aynı anahtara ikinci kez yazılan girdiyi öncekinin üzerine yazıyor; hangi girdinin
  "ikinci" olduğu `tara` çağrısına verilen argüman sırasına bağlı.
- **WV10.** Kap, çakışmayı denetlemiyor — ne bir uyarı basıyor ne bir istisna fırlatıyor.
  Kazananın kim olduğu yalnız iki kurulumu karşılaştırarak görülüyor.

`kapA`, `urun` ile `yedek`i bu sırayla tarıyor; `kapB` aynı iki denetleyiciyi ters sırayla.
`/urun/liste` isteğine verilen yanıt iki kurulumda **farklı**: `kapA`'da yedek denetleyicinin
yanıtı kazanıyor (son taranan o), `kapB`'de asıl denetleyicinin yanıtı kazanıyor (son taranan
o). Kaynak metinde hiçbir "kazanan budur" ifadesi yok; kazananı belirleyen tek şey `tara`
çağrısına verilen dizinin sırası.

Bu, önceki dersteki halka sıralamasıyla doğrudan karşıtlık kuruyor. Orada açıklamayla
bulunan halkaların sırası `oncelik` alanına göre belirleniyordu ve tarama sırasından tamamen
bağımsızdı — iki farklı yazım aynı sonucu üretiyordu. Burada aynı `tara` deseni kullanılıyor,
ama yönlendirme tablosu bir `Map` üzerine düz bir `put` ile kuruluyor ve **hiçbir eşitleyici
kural yok**. Fark, mekanizmanın kendisinde değil — ikisi de açıklama okuyan bir yansıma
taramasıdır — kabın çakışmayı çözecek açık bir kuralı olup olmamasında. Halka sıralamasında
`oncelik` alanı bu kuralı sağlıyordu; yönlendirme tablosunda böyle bir alan yok, bu yüzden
çakışma kaynaktaki hiçbir ifadeye değil, tarama sırasına düşüyor. **Bir açıklamanın
sıralamadan bağımsız olması otomatik gelmiyor; kabın o sıralamayı çözecek bir kuralı olması
gerekiyor ve bu kural yoksa kazananı belirleyen şey kaynaktaki en son satır değil, kabın
kendi kayıt sırasıdır.**

#### Elle Çağrılan Denetleyici Kap Kaydından Bağımsızdır

`urun.liste()` doğrudan çağrıldığında her zaman `"3 urun"` döner — bu, `UrunDenetleyici` sınıfının
kendi yönteminin davranışı ve hiçbir kap kaydına bağlı değil. `kapA` üzerinden `/urun/liste`
isteği ise `"yedek liste"` döndürüyor, çünkü o kurulumda son taranan `YedekDenetleyici` oldu. İki
yanıt farklı ve bu fark tam olarak dersin başındaki ayrımı gösteriyor: `urun` nesnesinin kendi
yöntemleri her zaman kendi mantığını çalıştırır, kap üzerinden gelen bir yanıt ise kabın o anki
kayıt durumuna bağlıdır. `UrunDenetleyici` sınıfının kendisi hiç değişmedi, `liste()` yöntemi hâlâ
aynı dizgeyi döndürüyor — değişen şey kabın hangi yöntemi `/urun/liste` yoluna bağladığı. Bir
denetleyici sınıfını birim testinde elle çağırmak bu yüzden yalnız o sınıfın kendi mantığını
doğruluyor; o sınıfın hangi yola bağlı olduğunu ve o yolun başka bir bileşenle çakışıp
çakışmadığını doğrulamıyor. İki soru ayrı: yöntem doğru mu ve kap onu doğru yola mı bağladı —
birincisini elle çağrı yanıtlıyor; ikincisini yalnız kabın kendisi çalıştırılarak
yanıtlanabiliyor.

Bu ayrımın kapsamı ilk derste görülenden biraz daha geniş: orada elle çağrı yalnız
varsayılan halkaları (`istek-kimligi`, `hata-yakalama`) kaçırıyordu, çünkü o davranış kabın
kendi kodunun bir parçasıydı. Burada kaçırılan şey bir varsayılan değil, kabın o an tuttuğu
**kayıt durumu** — hangi bileşenin hangi yolu son taradığı. Elle çağrı bu durumu hiç görmüyor
çünkü elle çağrı zaten kabı hiç sormuyor; nesnenin kendi belleğinde ne varsa onu çalıştırıyor.
İki tür bağımlılık da aynı kökten geliyor — kabın araya girip iş üzerinde bir karar vermesi —
ama biri kabın sabit davranışı, öteki kabın o an içinde bulunduğu duruma göre değişen bir
davranış.

#### Özet

- Dört uç noktanın dördü de `@UcNokta` açıklamasından doğdu; yönlendirme tablosunu kuran
  hiçbir çağrı kaynakta yok, tabloyu `tara` yönteminin okuduğu açıklamalar kuruyor.
- Eşleşmeyen bir yol istendiğinde kap istisna fırlatmadı, sabit bir varsayılan yanıt
  döndürdü; bu, kursun sessiz kusur sınıfının bir örneği.
- İki bileşen aynı yolu bildirdiğinde kap çakışmayı denetlemedi; kazanan, tarama çağrısına
  verilen sınıf sırasına göre değişti.
- Bu, önceki dersteki halka sıralamasından farklı: orada açıklama sırası kabın kendi
  kuralıyla (öncelik) sabitti, burada yol çakışmasını çözecek bir kural olmadığı için
  sonuç kayıt sırasına bağlı kaldı.
- Bir denetleyici sınıfını elle çağırmak yalnız o sınıfın kendi mantığını sınar; hangi yola
  bağlı olduğunu ve bir çakışmada kazanıp kazanmadığını sınamaz.

#### Sonraki Adım

Bu ders bir isteğin hangi yönteme ulaştığını ölçtü, ama o yönteme ulaşan verinin doğru
biçimde olup olmadığı hiç sorulmadı. Sıradaki ders bunu ele alıyor: bir alanın doğrulama
kuralının kaynakta bir çağrı mı yoksa bir açıklama mı olduğunu ve kural bozulduğunda ortak
hata yanıtının hangi alanları nereden topladığını ölçüyor.

### Doğrulama ve Hata İşleme

Source: https://academia.sh/tr/kurslar/kurumsal-uygulama-cercevesi/dogrulama-ve-hata-isleme

Dört alanın üçü doğrulama kuralını bir açıklamadan alıyor, biri hiç kural taşımıyor. Kural bozulduğunda ortak hata yanıtının zarfı nesne kabının varsayılanından, alan mesajları ise açıklamanın kendisinden geliyor; aynı geçersiz nesne kap dışında doğrudan işleyiciye verildiğinde hiçbir kural uygulanmıyor ve kusur sessiz kalıyor.

Önceki ders bir isteğin hangi yönteme ulaştığını ölçtü, ama o yönteme ulaşan verinin doğru
biçimde olup olmadığı hiç sorulmadı. Bu ders bunu ele alıyor: bir alanın doğrulama kuralının
kaynakta bir çağrı mı yoksa bir açıklama mı olduğunu ve kural bozulduğunda ortak hata
yanıtının hangi alanları nereden topladığını ölçüyor.

Doğrulama burada da aynı üç kaynak sorusuna tabi: bir alanın kuralı kaynakta bir ifadeyle mi
yazılı, yoksa yalnız bir işaretle mi belirtiliyor, yoksa kap alan hakkında hiçbir şey
söylemeden mi geçiyor? Bu derste dördüncü bir olasılık daha var — bir alanın **hiçbir**
kuralı olmaması — ve bu, önceki iki dersten farklı bir ölçüm ekliyor: kap yalnız kuralları
nereden aldığını değil, hangi alanların hiç kural taşımadığını da ortaya koyuyor.

Hata işleme de bu dersin konusu ve iki anlamda: bir isteğin **hangi** kuralı bozduğunu
toplamak (doğrulama) ve bu toplamı çağırana **nasıl** bir biçimde geri vermek (hata
işleme). İkisi ayrı sorular gibi görünse de aynı kapta yaşıyor — kap bir isteği reddederken
hem hangi alanların bozuk olduğunu bulmak hem de bunu tek bir tutarlı yanıta dönüştürmek
zorunda. Bu derste her iki iş de aynı `istek` yönteminde birleşiyor, ama kaynakları farklı
kalıyor: hangi kuralın bozulduğu açıklamadan geliyor, bozulan kuralların nasıl bir araya
toplanacağı ise kabın kendi kararı.

#### Dört Alanın Üçü Açıklamadan Kural Alıyor

- **WV11.** Kural kontrolü yalnız alan düzeyinde yapılıyor; yöntem parametreleri, iç içe
  nesneler ya da koleksiyon elemanları burada doğrulanmıyor.
- **WV12.** Bir alanın birden çok kuralı olabilir, ama bu derste her alan en çok bir kural
  taşıyor; kuralların birbirini nasıl sıraladığı ayrı bir ölçüm konusu.

`KayitIstegi` dört alan taşıyor: `ad`, `miktar`, `kategori`, `kod`. İlk üçü sırasıyla
`@ZorunluAlan`, `@PozitifDeger` ve `@Secenekler` açıklamalarını taşıyor; dördüncüsü, `kod`,
hiçbir açıklama taşımıyor. Kaynakta bu dört alanı doğrulayan hiçbir çağrı yok — `dogrula`
yöntemi hangi alanların hangi kurala tabi olduğunu bilmiyor, yalnız yansımayla her alanı
tek tek gözden geçirip üzerinde hangi açıklamanın durduğuna bakıyor. `kod` alanı için
hiçbir açıklama bulunmadığından, bu alan hiçbir zaman doğrulanmıyor — istenen herhangi bir
değer kabul ediliyor.

```java
// Kap.java — dogrulama kurallari aciklamadan doguyor, ortak hata yaniti kabin varsayilani
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.util.*;

public class Kap {

    @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD)
    @interface ZorunluAlan { String mesaj() default "alan bos olamaz"; }

    @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD)
    @interface PozitifDeger { String mesaj() default "deger sifirdan buyuk olmali"; }

    @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD)
    @interface Secenekler { String[] deger(); String mesaj() default "taninmayan secenek"; }

    interface Isleyici { String calistir(Object istek); }

    List<String> dogrula(Object nesne) throws ReflectiveOperationException {
        List<String> hatalar = new ArrayList<>();
        for (Field f : nesne.getClass().getDeclaredFields()) {
            f.setAccessible(true);
            Object deger = f.get(nesne);

            ZorunluAlan z = f.getAnnotation(ZorunluAlan.class);
            if (z != null && (deger == null || deger.toString().isBlank()))
                hatalar.add(f.getName() + ": " + z.mesaj());

            PozitifDeger p = f.getAnnotation(PozitifDeger.class);
            if (p != null && deger instanceof Integer i && i <= 0)
                hatalar.add(f.getName() + ": " + p.mesaj());

            Secenekler s = f.getAnnotation(Secenekler.class);
            if (s != null && !Arrays.asList(s.deger()).contains(deger))
                hatalar.add(f.getName() + ": " + s.mesaj());
        }
        return hatalar;
    }

    String istek(Object istekNesnesi, Isleyici isleyici) throws ReflectiveOperationException {
        List<String> hatalar = dogrula(istekNesnesi);
        if (!hatalar.isEmpty())
            return "hata; alan-sayisi=" + hatalar.size() + "; alanlar=" + hatalar;
        return isleyici.calistir(istekNesnesi);
    }

    static int kuralliAlanSayisi(Class<?> tip) {
        int n = 0;
        for (Field f : tip.getDeclaredFields())
            if (f.isAnnotationPresent(ZorunluAlan.class) || f.isAnnotationPresent(PozitifDeger.class)
                    || f.isAnnotationPresent(Secenekler.class)) n++;
        return n;
    }

    public static void main(String[] args) throws Exception {
        Kap kap = new Kap();
        Isleyici isleyici = istek -> {
            KayitIstegi k = (KayitIstegi) istek;
            return "kayit-tamam: ad=" + k.ad + ", miktar=" + k.miktar + ", kategori=" + k.kategori;
        };

        System.out.println("-- kacinin kurali aciklamadan geldi --");
        int toplamAlan = KayitIstegi.class.getDeclaredFields().length;
        int kuralliAlan = kuralliAlanSayisi(KayitIstegi.class);
        System.out.println("toplam alan: " + toplamAlan);
        System.out.println("aciklamayla kural tasiyan alan: " + kuralliAlan);

        KayitIstegi gecerli = new KayitIstegi("vida", 12, "elektronik", 501);
        System.out.println("-- gecerli istek --");
        System.out.println(kap.istek(gecerli, isleyici));

        KayitIstegi gecersiz = new KayitIstegi("", -3, "mobilya", 502);
        System.out.println("-- gecersiz istek: kap uzerinden --");
        System.out.println(kap.istek(gecersiz, isleyici));

        System.out.println("-- ayni gecersiz nesne, dogrudan isleyiciye verildi (kap devre disi) --");
        System.out.println(isleyici.calistir(gecersiz));
        System.out.println("kusur turu: sessiz (istisna yok, gecersiz veri isleyiciye ulasti)");
    }
}

class KayitIstegi {
    @Kap.ZorunluAlan String ad;
    @Kap.PozitifDeger int miktar;
    @Kap.Secenekler(deger = {"gida", "elektronik", "giyim"}) String kategori;
    long kod;

    KayitIstegi(String ad, int miktar, String kategori, long kod) {
        this.ad = ad;
        this.miktar = miktar;
        this.kategori = kategori;
        this.kod = kod;
    }
}
```

```
-- kacinin kurali aciklamadan geldi --
toplam alan: 4
aciklamayla kural tasiyan alan: 3
-- gecerli istek --
kayit-tamam: ad=vida, miktar=12, kategori=elektronik
-- gecersiz istek: kap uzerinden --
hata; alan-sayisi=3; alanlar=[ad: alan bos olamaz, miktar: deger sifirdan buyuk olmali, kategori: taninmayan secenek]
-- ayni gecersiz nesne, dogrudan isleyiciye verildi (kap devre disi) --
kayit-tamam: ad=, miktar=-3, kategori=mobilya
kusur turu: sessiz (istisna yok, gecersiz veri isleyiciye ulasti)
```

Dört alanın üçü kural taşıyor, biri taşımıyor. Bu oranın kendisi bir ölçüm: kaynağı okuyan
biri `kod` alanına bakıp hiçbir kısıtlama görmüyor ve bu doğru bir okuma — kural gerçekten
yok, kap onu hiç doğrulamıyor. Diğer üç alan için durum farklı: kaynakta `ad`'ın boş
olamayacağını söyleyen hiçbir `if` yok, `miktar`'ın sıfırdan büyük olması gerektiğini söyleyen
hiçbir karşılaştırma yok. Bu kurallar yalnız açıklamalarda yazılı ve onları okuyan `dogrula`
yöntemi olmadan hiçbir etkileri yok. Geçerli isteğin `kayit-tamam` ile sonuçlanması,
geçersiz isteğin ise üç ayrı hatayla dönmesi arasındaki fark, kodun hiçbir satırında açıkça
görünmeyen bu üç kuralın gerçekten çalıştığının kanıtı.

`kod` alanının kural taşımaması, bir eksiklik olarak da okunabilir, bilinçli bir seçim
olarak da. Kaynak metin bu ikisini ayırt etmiyor — bir alanın üzerinde açıklama yoksa, o
alanın "kasıtlı olarak serbest bırakıldığı" ile "kuralı yazmayı unuttuk" arasındaki fark
kaynakta hiçbir iz bırakmıyor. Önceki iki derste eksik olan şey bir davranıştı (bir halka,
bir uç nokta); burada eksik olan şey bir **kısıtlama** ve kısıtlamanın eksikliği kendini
hiçbir zaman bir hatayla göstermiyor — yalnız her değerin kabul edildiği bir sessizlikle.
Dört alanın üçünün kural taşıması, kalan birinin de aynı dikkatle gözden geçirildiği
anlamına gelmiyor; kural sayısı kadar, kuralsız bırakılan alanların bilinçli bir liste
olarak tutulması da bu yüzden ayrı bir disiplin gerektiriyor.

#### Ortak Hata Yanıtı: Zarf Varsayılan, İçerik Açıklama

- **WV13.** Hata yanıtının zarfı (`"hata; alan-sayisi=...; alanlar=..."` biçimi) `istek`
  yönteminin gövdesine yazılı; hiçbir açıklama bu biçimi belirlemiyor.

Geçersiz isteğe verilen yanıt iki parçadan oluşuyor ve ikisi de farklı bir kaynaktan
geliyor. Zarf — yanıtın kaç hata taşıdığını söyleyen ve hataları bir liste olarak saran
biçim — `Kap.istek` yönteminin kendi gövdesinde yazılı bir dizge kalıbı; bu, önceki
derslerdeki varsayılan davranışlarla aynı aileden: kaynakta hiçbir alan bunu istemedi, kap
her geçersiz istekte bu biçimi kendiliğinden üretiyor. İçerik ise farklı: `"ad: alan bos
olamaz"` ve `"kategori: taninmayan secenek"` dizgeleri doğrudan `ZorunluAlan` ile
`Secenekler` açıklamalarının `mesaj` alanından geliyor. Bu iki açıklama hiçbir özel mesaj
belirtmedi, bu yüzden kendi varsayılan mesajları kullanıldı — ama bir geliştirici
`@Kap.ZorunluAlan(mesaj = "ad girilmeden kayit olusturulamaz")` yazsaydı, yanıtta o metin
görünürdü. Zarf sabit, içerik açıklamanın elindedir.

Bu ayrım pratik bir sonuç doğuruyor: ortak hata yanıtının biçimini değiştirmek için (örneğin alan
sayısını en başa değil en sona koymak) kap'ın kendi kodu değiştirilmeli; hiçbir açıklama bunu
etkilemez. Ama tek bir alanın hata mesajını değiştirmek için kap'a hiç dokunmaya gerek yok —
açıklamanın `mesaj` alanı yeterli. İki değişiklik türü kaynakta iki ayrı yerde durur ve biri
diğerinden çok daha ucuz: mesaj metnini değiştirmek bir açıklama parametresi düzenlemektir, zarfı
değiştirmek kabın kendisini değiştirmektir.

Bu ayrımın bir de tersi var: `dogrula` yöntemi hataları topluyor ama hangi kuralın **önce**
kontrol edileceğine karar veriyor — burada alan bildirim sırasıyla aynı, `ad`, sonra
`miktar`, sonra `kategori`. Bu sıra da kabın kendi kararı; hiçbir açıklama "önce beni kontrol
et" demiyor. Sıralama görünüşte önemsiz duruyor, çünkü bu derste tüm kurallar birlikte
toplanıp tek bir yanıtta veriliyor — hiçbiri diğerini durdurmuyor. Ama bir kap "ilk hatada
dur" biçiminde tasarlansaydı, bu sıra hangi hatanın çağırana ulaşıp hangisinin hiç
görülmeyeceğini belirlerdi; o zaman sıra bir uygulama ayrıntısı olmaktan çıkıp davranışın
kendisi olurdu. Bu derste seçilen "hepsini topla" yaklaşımı bu riski taşımıyor, ama seçim
kabın kendisine ait — kaynakta hiçbir yerde "bütün hatalar toplansın" yazmıyor.

#### Kap Dışında Doğrulama Hiç Çalışmıyor

- **WV14.** `isleyici.calistir(gecersiz)` çağrısı `Kap.istek` yöntemini hiç görmüyor;
  `dogrula` hiçbir zaman çağrılmıyor.
- **WV15.** `kod` alanı zaten kural taşımadığı için hiçbir kurulumda doğrulanmıyor;
  aradaki fark yalnız diğer üç alanda görünüyor.

Son ölçüm aynı geçersiz nesneyi — boş `ad`, negatif `miktar`, tanınmayan `kategori` — kap'ı
hiç devreye sokmadan doğrudan işleyiciye veriyor. Sonuç: `kayit-tamam: ad=, miktar=-3,
kategori=mobilya`. Hiçbir istisna yok, hiçbir hata mesajı yok — işleyici geçersiz veriyle de
aynı sadakatle çalışıyor, çünkü onun kendi kodu hiçbir doğrulama içermiyor. Doğrulama
mantığı yalnız `Kap.istek` yönteminin içinde yaşıyor ve bu yöntem çağrılmadığı sürece üç
açıklamanın hiçbiri okunmuyor.

Bu, kursun tekrar eden sınırlayıcı kuralının bir başka örneği: bir kabın sağladığı garanti,
yalnız o kabın çağrıldığı yol üzerinde geçerlidir. `KayitIstegi` sınıfının üzerindeki
açıklamalar kaldırılmadı, hâlâ orada duruyor — ama onları okuyacak `dogrula` çağrısı devreye
girmediği sürece, sınıf sıradan bir veri taşıyıcısından farksız. Bu, önceki iki derste
görülenden biraz daha riskli bir sonuç doğuruyor, çünkü burada geçersiz veri yalnız yanlış
bir yanıt üretmiyor — geçerliymiş gibi görünen, "tamam" diyen bir yanıt üretiyor. Kural
bozulduğunda kap üzerinden geçen istek açıkça bir hata görüyor; kap dışından gelen aynı veri
ise hiçbir uyarı almadan "tamam" yanıtıyla devam ediyor. Doğrulamanın var olması, onu
uygulayan tek bir giriş noktası olduğu sürece bir güvence; birden fazla giriş noktası
olduğunda — burada kap üzerinden gelen ve doğrudan çağrılan iki yol — güvence yalnız
kullanılan yola bağlı kalıyor.

Bu, önceki iki dersteki sınırlayıcı ölçümlerle aynı köke iniyor ama sonucu bir derece daha ağır.
Birinci derste kap dışında kalan halka yalnız sırasını kaybediyordu; ikinci derste elle çağrılan
denetleyici yalnız kabın kayıt durumunu görmüyordu — ikisi de işlevsel olarak doğru çalışmaya
devam ediyordu. Burada ise kap dışında kalan çağrı, geçersiz veriyi **geçerliymiş gibi** işliyor:
`isleyici`'nin döndürdüğü dizge, doğru bir isteğin döndürdüğü dizgeyle aynı `kayit-tamam` önekini
taşıyor. Çağıran taraf yanıtın biçimine bakarak iki durumu ayırt edemiyor — ayırt edebilmek için
`ad` alanının boş olup olmadığını ayrıca kontrol etmesi gerekiyor, ki bu da doğrulamanın kap
dışında yeniden yazılması anlamına gelir. Bir uygulamada bu tür bir ikinci giriş noktası
genellikle bir arka plan işi, bir toplu içe aktarma adımı ya da bir başka bileşenin doğrudan
çağrısı biçiminde ortaya çıkıyor; hangi biçimde olursa olsun, kap onun varlığından habersiz ve
kural onun üzerinde hiç çalışmıyor.

#### Özet

- Dört alanın üçü (`ad`, `miktar`, `kategori`) doğrulama kuralını bir açıklamadan aldı;
  dördüncüsü (`kod`) hiçbir kural taşımadı ve hiç doğrulanmadı.
- Geçersiz istek kap üzerinden geçtiğinde ortak bir hata yanıtı üretti; yanıtın zarfı
  kabın kendi kodunda sabit, alan mesajları ise açıklamaların `mesaj` alanından geldi.
- Bir açıklamanın mesajını değiştirmek yalnız açıklamayı düzenlemeyi gerektirdi; zarfı
  değiştirmek kabın kendi kodunu değiştirmeyi gerektirirdi.
- Aynı geçersiz nesne kap dışında doğrudan işleyiciye verildiğinde hiçbir kural
  uygulanmadı ve işleyici "tamam" anlamına gelen bir yanıt üretti; kusur sessiz kaldı.
- Doğrulamanın güvencesi yalnız kabın çağrıldığı yol üzerinde geçerli; kap dışındaki bir
  çağrı aynı sınıfı hiçbir kısıtlama olmadan kullanabiliyor.

#### Sonraki Adım

Bu ders bir isteğin verisinin doğru olup olmadığını ölçtü, ama o veri işlendikten sonra
nereye yazıldığı hiç sorulmadı. Sıradaki ders bunu ele alıyor: bir depo arayüzünün
gerçekleştirimini kabın nasıl sağladığını ve aynı çağıranın yeniden derlenmeden iki ayrı
gerçekleştirimle nasıl koşabildiğini ölçüyor.

### Veri Erişim Soyutlaması

Source: https://academia.sh/tr/kurslar/kurumsal-uygulama-cercevesi/veri-erisim-soyutlamasi

Aynı derlenmiş çağıran kümesi — nesne kabı, servis ve giriş noktası — bir depo arayüzünün iki ayrı gerçekleştirimiyle, hiç yeniden derlenmeden koşturuluyor. Çağıranın sınıf dosyası yalnız arayüzü anıyor; aynı çağrı bir gerçekleştirimde sorunsuz çalışırken ötekinde bir istisnayla düşüyor, çünkü arayüz sözü ile gerçekleştirim davranışı ayrı ve kap bu ayrımı kapatmıyor, yalnız gizliyor.

Önceki ders bir isteğin verisinin doğru olup olmadığını ölçtü, ama o veri işlendikten sonra nereye
yazıldığı hiç sorulmadı. Bu ders bunu ele alıyor: bir **depo** arayüzünün gerçekleştirimini kabın
nasıl sağladığını ve aynı çağıranın yeniden derlenmeden iki ayrı gerçekleştirimle nasıl
koşabildiğini ölçüyor. Depo kavramı, bir iş mantığının kalıcılık ayrıntılarından bağımsız kalması
**M16/K04 Veri Erişim Katmanı ve İş Mantığı** kursunda kuruldu ve burada tekrarlanmıyor; bu dersin
sorusu ondan ayrı — kabın bu arayüzü hangi düzenekle doldurduğu.

Veritabanı burada da **modellenir**: gerçek bir bağlantı açılmaz, gerçek bir SQL ifadesi
çalıştırılmaz. `Depo` yalnız iki yöntem taşıyan bir arayüz — bir miktarı okumak, bir miktarı
yazmak — ve bu iki yöntemin arkasında ne durduğu çağıran için görünmez. Görünmezliğin ne
kadar gerçek olduğunu göstermenin tek yolu, aynı çağıranı gerçekten iki ayrı gerçekleştirimle
koşturmaktır; bu derste kullanılan yöntem tam olarak bu.

Önceki derslerde "aynı davranışı iki yolla koşturmak" kabın kurduğu nesneyle elle kurulan
nesneyi karşılaştırmak anlamına geliyordu. Bu ders aynı ilkeyi bir adım öteye taşıyor: burada
karşılaştırılan iki nesne değil, aynı çağıran kodun iki ayrı **gerçekleştirime** karşı
koşturulmasıdır. Bir arayüzün asıl sınandığı yer de burası — bir sınıfın davranışını taklit
etmek yetmiyor, aynı sözleşmeyi tutan tamamen farklı bir iç yapının da aynı çağıran kodu
memnun edip etmediği görülmesi gerekiyor.

#### Aynı Çağıran, İki Ayrı Gerçekleştirim

- **WV16.** Bu ölçüm gerçek bir sınıf yolu ayrımını sahneye koyar: arayüz, çağıran kod ve
  her gerçekleştirim ayrı dizinlere derlenir, sonra çalışma zamanında farklı kombinasyonlarla
  bir araya getirilir. Bir sınıfın hangi sınıf yolunda gerekmek zorunda olduğu **M08/K05
  Derleme, Test ve Ekosistem** kursunda ölçüldü; burada aynı düzenek arayüz–gerçekleştirim
  ayrımı için kullanılıyor; sayılar oradan tekrarlanmıyor.
- **WV17.** `Kap.kur` yöntemi gerçekleştirim sınıfının adını bir dizge parametresi olarak
  alıyor ve `Class.forName` ile buluyor; bu, çağıranın hiçbir gerçekleştirim sınıfını
  derleme zamanında anmadan bir örnek kurabilmesinin yolu.

`Kap`, `Servis` ve `Ana` sınıfları yalnız `Depo` arayüzüne karşı derleniyor — derleme sınıf
yoluna `BellekDepo` da `DiziDepo` da hiç girmiyor. Bu üç sınıf tek bir kez derleniyor, sonra
iki ayrı çalışma zamanı sınıf yoluyla, hiç yeniden derlenmeden koşturuluyor: birinde
gerçekleştirim `BellekDepo`, ötekinde `DiziDepo`. Aradaki fark yalnız `kos` çağrısına verilen
üçüncü sınıf yolu bileşeni ve `Ana.main`'e geçirilen gerçekleştirim adı.

```java
// IkiGercek.java — ayni derlenmis cagiran, arayuzu degistirmeden iki ayri gerceklestirimle kosuyor
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.net.*;
import java.nio.file.*;
import java.util.*;
import java.util.spi.ToolProvider;

public class IkiGercek {

    static Path yaz(Path dizin, String ad, String kaynak) throws IOException {
        Files.createDirectories(dizin);
        Path p = dizin.resolve(ad + ".java");
        Files.writeString(p, kaynak);
        return p;
    }

    static void derle(Path cikti, List<Path> sinifYolu, List<Path> kaynaklar) throws IOException {
        Files.createDirectories(cikti);
        List<String> arg = new ArrayList<>(List.of("-d", cikti.toString()));
        if (!sinifYolu.isEmpty()) {
            StringJoiner sj = new StringJoiner(File.pathSeparator);
            for (Path p : sinifYolu) sj.add(p.toString());
            arg.add("-classpath");
            arg.add(sj.toString());
        }
        for (Path k : kaynaklar) arg.add(k.toString());
        ToolProvider derleyici = ToolProvider.findFirst("javac").orElseThrow();
        StringWriter cikisYazi = new StringWriter();
        int sonuc = derleyici.run(new PrintWriter(cikisYazi), new PrintWriter(cikisYazi), arg.toArray(new String[0]));
        if (sonuc != 0) throw new RuntimeException("derleme basarisiz: " + cikisYazi);
    }

    static String kos(List<Path> sinifYolu, String gerceklestirimAdi) {
        URL[] url = new URL[sinifYolu.size()];
        try {
            for (int i = 0; i < url.length; i++) url[i] = sinifYolu.get(i).toUri().toURL();
            try (URLClassLoader yukleyici = new URLClassLoader(url, IkiGercek.class.getClassLoader().getPlatformClassLoader())) {
                Class<?> ana = Class.forName("Ana", true, yukleyici);
                ByteArrayOutputStream tampon = new ByteArrayOutputStream();
                PrintStream eski = System.out;
                System.setOut(new PrintStream(tampon));
                try {
                    ana.getMethod("main", String[].class).invoke(null, (Object) new String[]{gerceklestirimAdi});
                } finally {
                    System.setOut(eski);
                }
                return tampon.toString().strip();
            }
        } catch (InvocationTargetException e) {
            return "istisna: " + e.getCause().getClass().getSimpleName() + ": " + e.getCause().getMessage();
        } catch (Throwable t) {
            return "istisna: " + t.getClass().getSimpleName();
        }
    }

    public static void main(String[] args) throws Exception {
        Path kok = Files.createTempDirectory("ikigercek");
        Path kaynak = kok.resolve("kaynak");

        Path sArayuz = yaz(kaynak.resolve("arayuz"), "Depo",
                "public interface Depo {\n"
              + "  int miktarBul(String urun);\n"
              + "  void miktarGuncelle(String urun, int miktar);\n"
              + "}\n");

        Path sKap = yaz(kaynak.resolve("cagiran"), "Kap",
                "public class Kap {\n"
              + "  static Servis kur(String gerceklestirimAdi) throws ReflectiveOperationException {\n"
              + "    Class<?> c = Class.forName(gerceklestirimAdi);\n"
              + "    Depo depo = (Depo) c.getDeclaredConstructor().newInstance();\n"
              + "    return new Servis(depo);\n"
              + "  }\n"
              + "}\n");
        Path sServis = yaz(kaynak.resolve("cagiran"), "Servis",
                "public class Servis {\n"
              + "  private final Depo depo;\n"
              + "  public Servis(Depo depo) { this.depo = depo; }\n"
              + "  public String uygula(String urun, int degisim) {\n"
              + "    int mevcut = depo.miktarBul(urun);\n"
              + "    int yeni = mevcut + degisim;\n"
              + "    depo.miktarGuncelle(urun, yeni);\n"
              + "    return urun + \": \" + mevcut + \" -> \" + yeni;\n"
              + "  }\n"
              + "}\n");
        Path sAna = yaz(kaynak.resolve("cagiran"), "Ana",
                "public class Ana {\n"
              + "  public static void main(String[] args) throws Exception {\n"
              + "    Servis servis = Kap.kur(args[0]);\n"
              + "    System.out.println(servis.uygula(\"vida\", 5));\n"
              + "    System.out.println(servis.uygula(\"vida\", -2));\n"
              + "    System.out.println(servis.uygula(\"civi\", 3));\n"
              + "  }\n"
              + "}\n");

        Path sBellek = yaz(kaynak.resolve("bellek"), "BellekDepo",
                "import java.util.*;\n"
              + "public class BellekDepo implements Depo {\n"
              + "  private final Map<String, Integer> veri = new HashMap<>();\n"
              + "  public int miktarBul(String urun) { return veri.getOrDefault(urun, 0); }\n"
              + "  public void miktarGuncelle(String urun, int miktar) { veri.put(urun, miktar); }\n"
              + "}\n");
        Path sDizi = yaz(kaynak.resolve("dizi"), "DiziDepo",
                "public class DiziDepo implements Depo {\n"
              + "  private final String[] adlar = new String[1];\n"
              + "  private final int[] miktarlar = new int[1];\n"
              + "  private int dolu = 0;\n"
              + "  public int miktarBul(String urun) {\n"
              + "    for (int i = 0; i < dolu; i++) if (adlar[i].equals(urun)) return miktarlar[i];\n"
              + "    return 0;\n"
              + "  }\n"
              + "  public void miktarGuncelle(String urun, int miktar) {\n"
              + "    for (int i = 0; i < dolu; i++) if (adlar[i].equals(urun)) { miktarlar[i] = miktar; return; }\n"
              + "    if (dolu == adlar.length) throw new IllegalStateException(\"depo kapasitesi dolu: \" + urun + \" eklenemedi\");\n"
              + "    adlar[dolu] = urun; miktarlar[dolu] = miktar; dolu++;\n"
              + "  }\n"
              + "}\n");

        Path cArayuz = kok.resolve("cikti/arayuz");
        Path cCagiran = kok.resolve("cikti/cagiran");
        Path cBellek = kok.resolve("cikti/bellek");
        Path cDizi = kok.resolve("cikti/dizi");

        derle(cArayuz, List.of(), List.of(sArayuz));
        derle(cCagiran, List.of(cArayuz), List.of(sKap, sServis, sAna));
        derle(cBellek, List.of(cArayuz), List.of(sBellek));
        derle(cDizi, List.of(cArayuz), List.of(sDizi));

        System.out.println("-- ayni cagiran (Kap+Servis+Ana), BellekDepo gerceklestirimiyle --");
        System.out.println(kos(List.of(cCagiran, cArayuz, cBellek), "BellekDepo"));

        System.out.println("-- ayni cagiran, hic yeniden derlenmeden, DiziDepo gerceklestirimiyle --");
        System.out.println(kos(List.of(cCagiran, cArayuz, cDizi), "DiziDepo"));
        System.out.println("kusur turu: hata (istisna adiyla dustu, DiziDepo kapasitesini asan cagriyi reddetti)");
    }
}
```

```
-- ayni cagiran (Kap+Servis+Ana), BellekDepo gerceklestirimiyle --
vida: 0 -> 5
vida: 5 -> 3
civi: 0 -> 3
-- ayni cagiran, hic yeniden derlenmeden, DiziDepo gerceklestirimiyle --
istisna: IllegalStateException: depo kapasitesi dolu: civi eklenemedi
kusur turu: hata (istisna adiyla dustu, DiziDepo kapasitesini asan cagriyi reddetti)
```

`Servis` sınıfının kaynağında `BellekDepo` ya da `DiziDepo` adı hiçbir yerde geçmiyor —
yalnız `Depo depo` alanı var ve o alanın türü arayüz. `Kap.kur` da aynı şekilde: aldığı tek
bilgi bir dizge, `Class.forName` ile bulduğu sınıfı `Depo`'ya döküyor. Bu üç sınıf ilk
`derle` çağrısında, hiçbir gerçekleştirimin sınıf yolunda bulunmadığı bir ortamda derleniyor
ve derlenmiş hâlleri iki ayrı `kos` çağrısında tekrar kullanılıyor. Aradaki fark yalnız
`kos`'a verilen sınıf yolu listesinin üçüncü bileşeni.

Bu derlemenin gerçekten "yeniden derlenmeden" olduğunu doğrulayan şey, `derle(cCagiran, ...)`
çağrısının yalnız bir kez yapılması. `cCagiran` dizini bir kez üretiliyor ve iki `kos`
çağrısına da aynen veriliyor; ikinci `kos` çağrısı öncesinde `Kap`, `Servis` ya da `Ana`
kaynağına dönüp tekrar `derle` çağıran hiçbir satır yok. Gerçek bir kurulumda bu, bir
uygulamanın dağıtım paketinin (çağıran sınıfları içeren kısmı) bir kez üretilip, hangi
gerçekleştirimin bağlanacağının yalnız çalışma zamanı sınıf yoluyla belirlendiği anlamına
gelir — kaynak koddaki hiçbir değişiklik gerekmeden, hiçbir yeniden derleme adımı
çalıştırmadan, aynı `.class` dosyaları farklı bir depoya bağlanabiliyor.

#### Arayüz Sözü ile Gerçekleştirim Davranışı Ayrıdır

- **WV18.** `BellekDepo` bir `HashMap` üzerinde çalışıyor ve kaç farklı `urun` adı
  geldiğine bir sınır koymuyor. `DiziDepo` sabit uzunlukta iki dizi kullanıyor ve yalnız
  bir farklı `urun` adını tutabiliyor.
- **WV19.** `Depo` arayüzü bu kapasite farkını hiçbir yerde belirtmiyor; iki yöntemin
  imzası da bir sınır sözü taşımıyor.

İlk koşumda `vida` iki kez güncelleniyor (`0 -> 5`, sonra `5 -> 3`), ardından farklı bir
üründe (`civi`) bir kayıt daha açılıyor (`0 -> 3`). `BellekDepo` ile bu üç çağrı da sorunsuz
tamamlanıyor. `DiziDepo` ile aynı üç çağrı, aynı sırayla, aynı derlenmiş `Servis` sınıfı
üzerinden yapılıyor — ama üçüncüsü bir istisnayla düşüyor: `DiziDepo` yalnız bir farklı ürün
adını tutabiliyor ve `civi` ikinci bir ürün adı olarak geldiğinde kapasite doluyor.

Bu, dersin sınırlayıcı ölçümü: **arayüz sözü ile gerçekleştirim davranışı ayrıdır ve kap bu
ayrımı kapatmaz, yalnız gizler.** `Depo` arayüzü `miktarGuncelle`'nin her zaman başarılı
olacağını hiçbir yerde vaat etmiyor, ama pratikte `BellekDepo` ile çalışırken bu vaat hiç
test edilmeden doğru görünüyor — sınır hiç aşılmadığı için sorun hiç ortaya çıkmıyor. Aynı
kod `DiziDepo`'ya bağlandığında, arayüzün hiç söz vermediği bir kapasite sınırı aniden
görünür oluyor. Kap, `Servis`'in hangi gerçekleştirimle çalıştığını çağırandan gizliyor, ama
gerçekleştirimlerin kendi aralarındaki davranış farkını **ortadan kaldırmıyor** — yalnız
o farkın nerede ortaya çıkacağını, arayüzün arkasına, çalışma zamanına erteliyor.

Bu ayrımın bir de tersi var: iki gerçekleştirim de `vida` ürünü üzerindeki iki çağrıda
birebir aynı sonucu veriyor. Arayüzün gerçekten sağladığı şey bu — tek bir anahtarla yapılan
oku-yaz döngüsü, gerçekleştirimden bağımsız olarak aynı davranıyor. Sorun yalnız arayüzün
**söylemediği** bir varsayımın (sınırsız sayıda farklı anahtar) bir gerçekleştirimde geçerli,
ötekinde geçersiz olmasından doğuyor. Bir arayüz, üzerinde imzası bulunan yöntemlerin sözünü
verir; imzada yer almayan her şey — kapasite, eşzamanlılık, gecikme — gerçekleştirimin kendi
kararıdır ve arayüz onu hiç taşımaz.

Bu, kabın veri erişimi katmanındaki asıl katkısını da açıklığa kavuşturuyor. Kap, `Servis`
sınıfını hangi gerçekleştirimin dolduracağına karar veriyor, ama bu kararın **doğru**
gerçekleştirimi seçmiş olduğunu hiçbir yerde garanti etmiyor — `Kap.kur`, kendisine verilen
dizge adının gerçekten `Depo`'yu uygulayan bir sınıfı gösterdiğini bile yalnız çalışma
zamanında, `(Depo) c.getDeclaredConstructor().newInstance()` satırındaki döküm başarısız
olmadığı sürece anlıyor. Bir gerçekleştirimin arayüzün taşımadığı bir varsayımı ihlal etmesi
de aynı şekilde yalnız o varsayım gerçekten test edildiğinde ortaya çıkıyor — `DiziDepo` ile
tek bir ürün adı kullanan bir çağıran hiçbir zaman kapasite sınırını görmez. Bu derste
üçüncü çağrının farklı bir ürün adı taşıması bilinçli bir seçim: sınır yalnız iki farklı
anahtar aynı depoya ulaştığında görünür oluyor ve bu da arayüz testinin neden yalnız
"yöntem çağrılıyor mu" değil, "gerçekleştirimin gerçek sınırları nerede" sorusunu da
sorması gerektiğini gösteriyor.

#### Özet

- `Kap`, `Servis` ve `Ana` sınıfları yalnız `Depo` arayüzüne karşı bir kez derlendi ve hiç
  yeniden derlenmeden iki ayrı gerçekleştirimle (`BellekDepo`, `DiziDepo`) koşturuldu.
- Çağıranın sınıf dosyasında hiçbir gerçekleştirim sınıfının adı geçmiyor; `Kap.kur`
  gerçekleştirimi yalnız bir dizge adıyla, yansımayla buluyor.
- Aynı üç çağrı `BellekDepo` ile sorunsuz tamamlandı, `DiziDepo` ile üçüncü çağrıda bir
  `IllegalStateException` ile düştü — aynı derlenmiş kod, aynı sıradaki çağrılar, iki ayrı
  sonuç.
- `Depo` arayüzü kapasite gibi imzada yer almayan hiçbir garantiyi taşımıyor; bu tür
  farklar yalnız gerçekleştirim değiştiğinde ortaya çıkıyor.
- Kap, çağırandan gerçekleştirim seçimini gizliyor ama gerçekleştirimler arasındaki
  davranış farkını ortadan kaldırmıyor; farkı yalnız çalışma zamanına erteliyor.

#### Sonraki Adım

Bu ders bir arayüzün arkasında hangi gerçekleştirimin durduğunu ölçtü, ama gerçekleştirimin
kendi alanlarının nesne ile depo arasında nasıl eşleştiği hiç sorulmadı. Sıradaki ders bunu
ele alıyor: bir nesnenin alanları ile bir tablonun sütunları arasındaki bağlamanın kaynakta
bir açıklamadan geldiğini ve erişilmeyen bir alanın hiç yüklenmediğini ölçüyor.

### Nesne ve Tablo Bağlaması

Source: https://academia.sh/tr/kurslar/kurumsal-uygulama-cercevesi/nesne-ve-tablo-baglamasi

Bir varlık nesnesinin dört alanından ikisi yükleme anında, ikisi ilk erişimde yükleniyor. İkinci erişim tabloyu tekrar okumuyor, önbellekten dönüyor; ama nesne kaptan ayrıldıktan sonra hiç erişilmemiş bir alana erişildiğinde tablo okunamıyor ve kusur sessizce boş bir değer olarak dönüyor.

Önceki ders bir arayüzün arkasında hangi gerçekleştirimin durduğunu ölçtü, ama gerçekleştirimin
kendi alanlarının nesne ile depo arasında nasıl eşleştiği hiç sorulmadı. Bu ders bunu ele
alıyor: bir nesnenin alanları ile bir tablonun sütunları arasındaki bağlamanın kaynakta bir
açıklamadan geldiğini ve erişilmeyen bir alanın hiç yüklenmediğini ölçüyor. Kursun terim
kararında bu bağlantı **nesne–tablo bağlaması**dır; nesnenin kendisi ise bir **varlık
nesnesi**.

Tablo burada da **modellenir**: gerçek bir veritabanı bağlantısı açılmaz, gerçek bir sorgu
çalıştırılmaz. Bir "tablo", satır kimliğine göre erişilen bir sütun adı–değer yapısından
ibaret — `Map<Integer, Map<String, Object>>`. Bir varlık nesnesi bu satırlardan biriyle
bağlanır ve nesnenin hangi alanının hangi sütuna karşılık geldiği kaynakta iki açıklamayla
belirtiliyor:
`@Sutun` her zaman yüklenen alanlar için, `@TembelSutun` yalnız erişildiğinde yüklenen alanlar
için.

Bu ayrımın bir maliyet sorusu olduğu unutulmamalı. Gerçek bir depoda her sütun bir disk
erişimi, bir ağ gecikmesi taşıyabilir; bir satırın yalnız iki sütununu her zaman okuyup
kalanını yalnız gerektiğinde okumak, bir nesnenin **hiç kullanılmayacak** alanları için hiç
ödeme yapmamak anlamına gelir. Bu derste maliyetin kendisi ölçülmüyor — süre ve gecikme
yazılmıyor — ama maliyetin **nereye düştüğü** ölçülüyor: `yukSirasi` listesi, hangi sütunun
ne zaman okunduğunu tutan bir günlük ve bu günlük hem yükleme anındaki hem erişim anındaki
her okumayı aynı biçimde kaydediyor.

#### Yükleme Anında İki Alan, Erişimde İki Alan

- **WV20.** `UrunKarti` dört alan taşıyor: `ad` ve `miktar` `@Sutun`, `aciklamaOnbellek` ve
  `resimYoluOnbellek` `@TembelSutun`. Alan adları sütun adlarıyla birebir aynı olmak
  zorunda değil; açıklamanın `ad` parametresi eşleşmeyi kaynaktaki alan adından bağımsız
  kılıyor.
- **WV21.** Tembel alanların gerçek değeri bir "önbellek" alanında tutuluyor; ilk kez `null`
  olan bu alan, ilk başarılı yüklemeden sonra bir daha hiç sıfırlanmıyor.

`Kap.yukle` bir satırı bir nesneye dönüştürürken yalnız `@Sutun` taşıyan alanları dolduruyor;
`@TembelSutun` taşıyanlara hiç dokunmuyor. Nesne, hangi nesne kabına ve hangi satıra ait olduğunu
`VarlikNesnesi` üst sınıfından gelen `oturum` ve `satirId` alanlarıyla taşıyor — bu ikisi `@Sutun`
değil, nesne kabının kendi kurduğu bir bağ.

```java
// Kap.java — nesne-tablo baglamasi aciklamadan doguyor, tembel alan erisim aninda yukleniyor
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.util.*;

public class Kap {

    @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE)
    @interface Tablo { String ad(); }

    @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD)
    @interface Sutun { String ad(); }

    @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD)
    @interface TembelSutun { String ad(); }

    final Map<Integer, Map<String, Object>> tablo = new LinkedHashMap<>();
    final List<String> yukSirasi = new ArrayList<>();

    void satirEkle(int id, Map<String, Object> sutunlar) { tablo.put(id, sutunlar); }

    Object sutunOku(int id, String sutunAdi, String etiket) {
        yukSirasi.add(sutunAdi + ": " + etiket);
        return tablo.get(id).get(sutunAdi);
    }

    <T> T yukle(Class<T> tip, int id) throws ReflectiveOperationException {
        T nesne = tip.getDeclaredConstructor().newInstance();
        for (Field f : tip.getDeclaredFields()) {
            f.setAccessible(true);
            Sutun s = f.getAnnotation(Sutun.class);
            if (s != null) f.set(nesne, sutunOku(id, s.ad(), "yukleme-aninda"));
        }
        Field oturumAlani = tip.getSuperclass().getDeclaredField("oturum");
        Field idAlani = tip.getSuperclass().getDeclaredField("satirId");
        oturumAlani.setAccessible(true);
        idAlani.setAccessible(true);
        oturumAlani.set(nesne, this);
        idAlani.set(nesne, id);
        return nesne;
    }

    void ayril(VarlikNesnesi nesne) throws ReflectiveOperationException {
        Field oturumAlani = VarlikNesnesi.class.getDeclaredField("oturum");
        oturumAlani.setAccessible(true);
        oturumAlani.set(nesne, null);
    }

    public static void main(String[] args) throws Exception {
        Kap kap = new Kap();
        kap.satirEkle(1, Map.of("ad", "vida", "miktar", 12, "aciklama", "6mm somunlu vida", "resimYolu", "gorsel/vida.png"));

        System.out.println("-- kap ile yukleme: sadece iki sutun aninda okunuyor --");
        UrunKarti urun = kap.yukle(UrunKarti.class, 1);
        System.out.println("yukleme sonrasi yuk sirasi: " + kap.yukSirasi);
        System.out.println("toplam alan: 4, yukleme aninda okunan: "
                + kap.yukSirasi.stream().filter(s -> s.endsWith("yukleme-aninda")).count());

        System.out.println("-- tembel alana erisim: ilk cagri tabloyu okuyor --");
        System.out.println("aciklama: " + urun.aciklama());
        System.out.println("aciklama tekrar: " + urun.aciklama());
        System.out.println("yuk sirasi (aciklama sonrasi): " + kap.yukSirasi);
        System.out.println("aciklama icin kac kez tablo okundu: "
                + kap.yukSirasi.stream().filter(s -> s.startsWith("aciklama:") && s.endsWith("erisim-aninda")).count());
        System.out.println("resimYolu hic erisilmedi, kayitta hic gecmiyor: "
                + kap.yukSirasi.stream().noneMatch(s -> s.startsWith("resimYolu")));

        System.out.println("-- kaptan ayrilma sonrasi hic erisilmemis tembel alan --");
        kap.ayril(urun);
        System.out.println("resimYolu (kap ayrildiktan sonra ilk erisim): '" + urun.resimYolu() + "'");
        System.out.println("kusur turu: sessiz (istisna yok, bos deger sessizce dondu)");

        System.out.println("-- elle kurulan nesne: kap hic devrede degil --");
        UrunKarti elleKurulan = new UrunKarti();
        System.out.println("elle kurulan nesnede aciklama ilk erisimde: '" + elleKurulan.aciklama() + "'");
    }
}

class VarlikNesnesi {
    transient Kap oturum;
    transient int satirId;

    Object tembelOku(String sutunAdi, String onbellek) {
        if (onbellek != null) return onbellek;
        if (oturum == null) return "";
        return String.valueOf(oturum.sutunOku(satirId, sutunAdi, "erisim-aninda"));
    }
}

@Kap.Tablo(ad = "urun_karti")
class UrunKarti extends VarlikNesnesi {
    @Kap.Sutun(ad = "ad") String ad;
    @Kap.Sutun(ad = "miktar") int miktar;
    @Kap.TembelSutun(ad = "aciklama") String aciklamaOnbellek;
    @Kap.TembelSutun(ad = "resimYolu") String resimYoluOnbellek;

    String aciklama() {
        aciklamaOnbellek = (String) tembelOku("aciklama", aciklamaOnbellek);
        return aciklamaOnbellek;
    }

    String resimYolu() {
        resimYoluOnbellek = (String) tembelOku("resimYolu", resimYoluOnbellek);
        return resimYoluOnbellek;
    }
}
```

```
-- kap ile yukleme: sadece iki sutun aninda okunuyor --
yukleme sonrasi yuk sirasi: [ad: yukleme-aninda, miktar: yukleme-aninda]
toplam alan: 4, yukleme aninda okunan: 2
-- tembel alana erisim: ilk cagri tabloyu okuyor --
aciklama: 6mm somunlu vida
aciklama tekrar: 6mm somunlu vida
yuk sirasi (aciklama sonrasi): [ad: yukleme-aninda, miktar: yukleme-aninda, aciklama: erisim-aninda]
aciklama icin kac kez tablo okundu: 1
resimYolu hic erisilmedi, kayitta hic gecmiyor: true
-- kaptan ayrilma sonrasi hic erisilmemis tembel alan --
resimYolu (kap ayrildiktan sonra ilk erisim): ''
kusur turu: sessiz (istisna yok, bos deger sessizce dondu)
-- elle kurulan nesne: kap hic devrede degil --
elle kurulan nesnede aciklama ilk erisimde: ''
```

`urun.yukSirasi`'nin ilk hâli yalnız iki girdi taşıyor: `ad` ve `miktar`. `aciklama` ile
`resimYolu` bu listede hiç görünmüyor, çünkü `yukle` onları hiç okumadı — dört alanlı bir
nesne, tablonun yalnız iki sütununu okuyarak kuruldu. Bu, kaynak metinle çelişen bir sonuç
değil, kaynağın **tam olarak söylediği** şey: `@Sutun` yalnız iki alanda var, `@TembelSutun`
diğer ikisinde ve `yukle` yalnız `@Sutun` taşıyanları dolduruyor. Ama satırı okuyan biri
`UrunKarti`'nin dört alanının da bir tablo satırından geldiğini bilir; hangi ikisinin **ne
zaman** geldiğini bilmek için `Kap.yukle` yönteminin gövdesini okumak gerekiyor.

#### Tembel Alan Yalnız Bir Kez Yükleniyor

- **WV22.** `aciklama()` yöntemi çağrıldığında önce kendi önbellek alanına bakıyor; alan
  doluysa tabloya hiç gitmiyor. Bu, yöntemin adının "tembel" olmasının ikinci anlamı: yalnız
  ilk erişimde değil, en fazla **bir kez** yükleniyor.

`aciklama()` iki kez çağrılıyor ve `yuk sirasi` listesine yalnız bir `"aciklama:
erisim-aninda"` girdisi ekleniyor. İlk çağrı `aciklamaOnbellek` alanının `null` olduğunu
görüyor, tabloyu okuyor, sonucu aynı alana yazıyor. İkinci çağrı aynı alanın artık dolu
olduğunu görüyor ve tabloya hiç gitmiyor. Bu, kap içindeki tekil kapsamla aynı aileden bir
davranış — orada bir bileşenin ikinci isteği aynı örneği döndürüyordu, burada bir alanın
ikinci erişimi aynı değeri döndürüyor — ama kaynağı farklı: tekillik kabın kendi kural
tablosundaydı, burada önbellekleme `tembelOku` yönteminin kendi gövdesinde yazılı bir kontrol.
`resimYolu` alanına hiç erişilmediği için `yuk sirasi` listesinde bu sütun için hiçbir girdi
yok — dört alanlı nesnenin dördüncü alanı, program sonuna kadar tabloya hiç sorulmadan
kalabiliyor. Bu, tembel yüklemenin asıl kazancı: bir nesnenin bütün alanlarını taşıması,
hepsinin gerçekten okunacağı anlamına gelmiyor ve okunmayan alan için hiçbir maliyet
ödenmiyor.

Önbellekleme yalnız bir performans kararı değil, bir tutarlılık kararı da taşıyor. Tabloya
`aciklama()` çağrısı arasında başka bir yol aynı satırın `aciklama` sütununu değiştirseydi,
`urun` nesnesi bunu asla görmezdi — önbellek dolduktan sonra `tembelOku` bir daha tabloya
hiç bakmıyor. Bu ders bu senaryoyu koşturmuyor, ama sonucu kaynaktan çıkarılabilir: bir
tembel alan yalnız **ilk okunduğu anki** değeri taşıyor, o andan sonra tablo değişse de nesne
değişmiyor. Bir varlık nesnesini uzun süre elde tutan bir çağıran için bu, yükleme anının
gerçek bir anlık görüntü olduğu ve o görüntünün süresiz doğru kalacağının hiçbir yerde
vaat edilmediği anlamına geliyor.

#### Kap Dışında Tembel Yükleme Çalışmıyor: Sessiz Bir Boşluk

- **WV23.** `ayril` yalnız `oturum` alanını `null` yapıyor; nesnenin diğer alanları,
  önbelleğe alınmış olan `ad` ve `miktar` dahil, hiç değişmiyor.
- **WV24.** `tembelOku`, `oturum == null` gördüğünde bir istisna fırlatmak yerine boş bir
  dizge döndürüyor. Bu, dersin ikinci sessiz kusuru: erişim başarısız olmuyor, yalnız
  yanlış bir değer üretiyor.

`kap.ayril(urun)` çağrısı, `urun`'un kabın oturumuyla bağını kesiyor — gerçek bir uygulamada
bu, bir isteğin işlenmesi bittikten sonra nesnenin kap dışına, örneğin bir başka katmana
taşınmasının karşılığı. Bu noktaya kadar `resimYolu` hiç erişilmemişti; `ayril`'dan sonra ilk
kez çağrıldığında `tembelOku` artık `oturum`'un `null` olduğunu görüyor ve tabloya
sorabileceği hiçbir yol kalmıyor. Yöntem bir istisna fırlatmak yerine boş bir dizge
döndürüyor ve çağıran taraf bu boş dizgeyi gerçek bir "resim yolu yok" durumundan ayırt
edemiyor. Kusur burada da **sessiz**: program çökmüyor, hiçbir uyarı basmıyor, yalnız yanlış
bir değerle devam ediyor.

Bu, dersin sınırlayıcı ölçümü: **tembel yükleme kabın dışında çalışmaz; nesne kaptan
çıktıktan sonra erişilen alan boş kalır.** `ad` ve `miktar` bu sorunu yaşamıyor, çünkü ikisi
de yükleme anında dolduruldu ve `ayril`'dan etkilenmiyor — sorun yalnız **hiç erişilmemiş**
tembel alanlarda ortaya çıkıyor. Bu da yükleme stratejisinin gizli bir kuralını gösteriyor:
bir varlık nesnesinin kap içinde ne kadar süre kaldığı, hangi tembel alanlarına erişildiğini
belirliyor ve erişim penceresi kapandıktan sonra erişilmemiş kalan her tembel alan bir daha
asla doğru değerine ulaşamıyor.

Son ölçüm bu kuralı en baştan kuran bir nesneyle sınıyor: `new UrunKarti()` ile elle kurulan
nesnenin `oturum` alanı hiçbir zaman bir kaba bağlanmadı, baştan `null`. Bu nesnede
`aciklama()` çağrıldığında sonuç yine boş dizge — `ayril` çağrılan nesneyle birebir aynı
davranış, aynı sessiz sonuç. İki yol da aynı noktaya çıkıyor: nesne kabın oturumuyla ya hiç
bağlanmadı ya da bağı sonradan koptu ve her iki durumda da tembel alan erişimi aynı boş
değerle sonuçlanıyor. `@TembelSutun` açıklaması `UrunKarti` sınıfının üzerinde durmaya devam
ediyor — kaldırılmadı — ama onu yerine getirecek bir oturum yoksa, önceki derslerde görülen
kuralın bir kez daha doğrulanması: açıklama bir istektir, isteği karşılayacak taraf yoksa
hiçbir şey olmaz.

Bu ikisi arasındaki tek fark, kusurun **ne kadar sürpriz** olduğu. Elle kurulan bir nesnede kap
hiç devrede olmadığı zaten baştan belli — bir geliştirici bu nesneyi `new` ile kurarken tembel
yüklemenin çalışmayacağını beklemek zorunda. Ama `ayril`'dan geçen nesne az önce kabın içindeydi;
`ad` ve `miktar` alanları doğru değerlerini taşıyor ve nesne her bakımdan "gerçek" görünüyor. Bu
görünüş, `resimYolu` gibi hiç erişilmemiş bir alanın da aynı ölçüde güvenilir olduğu izlenimini
veriyor — oysa o alan, nesnenin görünüşte tamamlanmış olmasına rağmen hâlâ kabın oturumuna
bağımlı. Sınırlayıcı ölçümün asıl değeri burada: sorun rastgele bir alanda değil, tam olarak **kap
dışına en son çıkan** ve **hiç dokunulmamış** alanlarda ortaya çıkıyor ve bu ikisinin kesişimi
kaynak metinde hiçbir yerde işaretli değil.

#### Özet

- `UrunKarti`'nin dört alanından ikisi (`ad`, `miktar`) yükleme anında, ikisi
  (`aciklamaOnbellek`, `resimYoluOnbellek`) yalnız ilk erişimde tablodan okundu.
- Bir tembel alana ikinci erişim tabloya hiç gitmedi; önbellek alanı doluysa `tembelOku`
  doğrudan onu döndürdü.
- Hiç erişilmeyen bir tembel alan (`resimYolu`) programın sonuna kadar tabloya hiç
  sorulmadı; bu, tembel yüklemenin okunmayan alan için maliyet ödetmeyen tarafı.
- Nesne kaptan ayrıldıktan sonra hiç erişilmemiş bir tembel alana erişildiğinde tabloya
  ulaşılamadı ve yöntem istisna yerine boş bir dizge döndürdü; kusur sessiz kaldı.
- Elle kurulan bir nesnede aynı tembel alana erişim, kaptan ayrılan nesneyle birebir aynı
  boş sonucu verdi; ikisinde de eksik olan şey kabın oturumuydu.

#### Sonraki Adım

Bu ders bir nesnenin alanlarının ne zaman yüklendiğini ölçtü, ama bu yüklemenin ve
üzerindeki değişikliklerin nerede başlayıp nerede bittiği hiç sorulmadı. Sıradaki ders bunu
ele alıyor: bildirimsel bir işlem sınırının kaynakta bir açıklama olduğunu ve bir bileşenin
kendi kendini çağırmasının bu sınırı nasıl atladığını ölçüyor.

### İşlem Yönetimi

Source: https://academia.sh/tr/kurslar/kurumsal-uygulama-cercevesi/islem-yonetimi

İki çağrı yolu yan yana koşturuluyor ve bildirimsel işlem sınırının kaç kez açıldığı sayılıyor. Bir bileşenin kendi kendini çağırması vekili atlıyor: iç çağrı için sınır hiç açılmıyor, hiçbir hata çıkmıyor ve bu sessizlik kaynakta hiçbir yerde işaretli değil.

Önceki ders bir nesnenin alanlarının ne zaman yüklendiğini ölçtü, ama bu yüklemenin ve üzerindeki
değişikliklerin nerede başlayıp nerede bittiği hiç sorulmadı. Bu ders bunu ele alıyor: bildirimsel
bir işlem sınırının kaynakta bir açıklama olduğunu ve bir bileşenin kendi kendini çağırmasının bu
sınırı nasıl atladığını ölçüyor. İşlemin kendisi — bir dizi değişikliğin ya tamamen ya hiç
uygulanmaması ve yalıtım düzeyleri — **M17/K04 İlişkisel Veritabanı Yönetimi** kursunda kavram
olarak kuruldu ve burada tekrarlanmıyor; bu dersin sorusu ondan ayrı: sınırın **nerede açılıp
kapandığı** ve bu sınırı neyin çizdiği.

Gerçek bir veritabanı bağlantısı burada da yok. Bir **işlem sınırı**, `sinirAcilan` adlı bir
sayacın artması olarak modelleniyor — gerçek bir işlemde bu, bir bağlantının işlem kipine
alınması ve sonunda onaylanması ya da geri alınmasıdır; burada yalnız "sınır kaç kez açıldı"
sorusu ölçülüyor, açılan sınırın içinde ne olduğu değil. Bildirimsel sınır, kaynakta hiçbir
`baslatIslemi()` çağrısı olmadan, yalnız bir yöntemin üzerindeki `@Islem` açıklamasından
doğuyor.

"Bildirimsel" sözcüğü tam olarak bunu anlatıyor: sınırı çizen taraf, yöntemi çağıran kod değil,
yöntemin üzerinde duran açıklama. Önceki bir ders bu bildirim biçimini doğrulama kurallarında
görmüştü — bir alanın kısıtlaması da açıklamada duruyordu; onu okuyan taraf `Kap`'tı. İşlem
sınırında fark şu: doğrulamanın uygulanıp uygulanmaması yalnız hangi yöntemin çağrıldığına
bağlıydı, işlem sınırının açılıp açılmaması ise çağrının **nereden geldiğine** de bağlı. Bu fark,
dersin geri kalanının asıl konusu.

#### İki Çağrı Yolu, İki Ayrı Sınır Sayısı

- **WV25.** `@Islem`, hem `yatir` hem `yatirVeBildir` yönteminin üzerinde duruyor. İkisi de
  aynı gerçekleştirim sınıfının üyesi.
- **WV26.** Vekil, çağrılan yöntemi hedef nesnenin kendi sınıfında yeniden arayıp
  `@Islem` açıklamasını orada kontrol ediyor; bu, `m.invoke` çağrısından **önce** yapılıyor,
  böylece sınır açma kararı gerçek çağrıdan önce veriliyor.

`HesapGerceklestirimi.yatirVeBildir` kendi içinde `yatir(miktar)`'ı çağırıyor — bu, aynı
nesnenin bir yöntemi kendi başka bir yöntemini çağırması, yani bir **iç çağrı**. `Kap.sarmala`
bu iki yöntemi de bir vekil arkasına koyuyor ve vekil yalnız **dışarıdan** gelen çağrıları
görüyor.

Ölçüm iki ayrı kurulumla yapılıyor ve ikisi de aynı gerçekleştirim sınıfını kullanıyor.
Kurulumlar arasındaki tek fark, dışarıdaki çağıranın hangi yöntemleri, kaç kez çağırdığı;
`HesapGerceklestirimi`'nin kendi kaynak kodu iki kurulumda da birebir aynı kalıyor. Bu, önceki
derslerdeki "iki kurulum" ölçümlerinden bir bakımdan farklı: orada değişen şey genellikle
tarama sırası ya da gerçekleştirim seçimiydi, burada değişen şey yalnız **dışarıdan gelen
çağrı deseni**.

```java
// Kap.java — bildirimsel islem siniri aciklamadan doguyor, kendi kendini cagirma vekili atliyor
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.util.*;

public class Kap {

    @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD)
    @interface Islem {}

    static int sinirAcilan = 0;
    static List<String> gunluk = new ArrayList<>();

    @SuppressWarnings("unchecked")
    static <T> T sarmala(T hedef, Class<T> arayuz) {
        return (T) Proxy.newProxyInstance(
                Kap.class.getClassLoader(),
                new Class<?>[]{arayuz},
                (v, m, args) -> {
                    Method gercek = hedef.getClass().getMethod(m.getName(), m.getParameterTypes());
                    if (gercek.isAnnotationPresent(Islem.class)) {
                        sinirAcilan++;
                        gunluk.add("sinir-acildi: " + m.getName());
                    }
                    try {
                        return m.invoke(hedef, args);
                    } catch (InvocationTargetException e) {
                        throw e.getCause();
                    }
                });
    }

    public static void main(String[] args) throws Exception {
        HesapGerceklestirimi gercek = new HesapGerceklestirimi();
        Hesap vekil = sarmala(gercek, Hesap.class);

        System.out.println("-- yol A: yalniz yatirVeBildir digaridan cagriliyor --");
        System.out.println(vekil.yatirVeBildir(100));
        System.out.println("sinir acilma sayisi: " + sinirAcilan);
        System.out.println("gunluk: " + gunluk);

        sinirAcilan = 0;
        gunluk.clear();
        gercek = new HesapGerceklestirimi();
        vekil = sarmala(gercek, Hesap.class);

        System.out.println("-- yol B: yatir ve yatirVeBildir ayri ayri digaridan cagriliyor --");
        System.out.println(vekil.yatir(50));
        System.out.println(vekil.yatirVeBildir(100));
        System.out.println("sinir acilma sayisi: " + sinirAcilan);
        System.out.println("gunluk: " + gunluk);

        System.out.println("-- ic cagrinin kendisi vekili hic gormedi --");
        System.out.println("HesapGerceklestirimi.yatirVeBildir icindeki 'yatir(miktar)' cagrisi "
                + "'this' uzerinden yapiliyor, vekil uzerinden degil");
        System.out.println("dogrulama: yol B'de disaridan iki cagri oldu, ic cagriyla birlikte "
                + "toplamda uc kez yatir/yatirVeBildir calisti, ama sinir yalniz iki kez acildi");
        System.out.println("kusur turu: sessiz (istisna yok, ic cagri icin sinir hic acilmadan calisti)");

        System.out.println("-- elle kurulan nesne: kap hic devrede degil --");
        HesapGerceklestirimi elleKurulan = new HesapGerceklestirimi();
        sinirAcilan = 0;
        System.out.println(elleKurulan.yatirVeBildir(100));
        System.out.println("sinir acilma sayisi (vekil hic kurulmadi): " + sinirAcilan);
    }
}

interface Hesap {
    String yatir(int miktar);
    String yatirVeBildir(int miktar);
}

class HesapGerceklestirimi implements Hesap {
    int bakiye = 0;

    @Kap.Islem
    public String yatir(int miktar) {
        bakiye += miktar;
        return "bakiye=" + bakiye;
    }

    @Kap.Islem
    public String yatirVeBildir(int miktar) {
        String sonuc = yatir(miktar);
        return sonuc + "; bildirildi";
    }
}
```

```
-- yol A: yalniz yatirVeBildir digaridan cagriliyor --
bakiye=100; bildirildi
sinir acilma sayisi: 1
gunluk: [sinir-acildi: yatirVeBildir]
-- yol B: yatir ve yatirVeBildir ayri ayri digaridan cagriliyor --
bakiye=50
bakiye=150; bildirildi
sinir acilma sayisi: 2
gunluk: [sinir-acildi: yatir, sinir-acildi: yatirVeBildir]
-- ic cagrinin kendisi vekili hic gormedi --
HesapGerceklestirimi.yatirVeBildir icindeki 'yatir(miktar)' cagrisi 'this' uzerinden yapiliyor, vekil uzerinden degil
dogrulama: yol B'de disaridan iki cagri oldu, ic cagriyla birlikte toplamda uc kez yatir/yatirVeBildir calisti, ama sinir yalniz iki kez acildi
kusur turu: sessiz (istisna yok, ic cagri icin sinir hic acilmadan calisti)
-- elle kurulan nesne: kap hic devrede degil --
bakiye=100; bildirildi
sinir acilma sayisi (vekil hic kurulmadi): 0
```

Yol A'da tek bir dış çağrı var — `vekil.yatirVeBildir(100)` — ve sınır bir kez açılıyor. Yol
B'de iki dış çağrı var — `vekil.yatir(50)` ve `vekil.yatirVeBildir(100)` — ve sınır iki kez
açılıyor. İkisi arasındaki fark yalnız **dışarıdan kaç çağrı yapıldığı**; `yatirVeBildir`'in
kendi içinde `yatir`'ı çağırması hiçbir kurulumda sınır sayısına eklenmiyor. Bu, kursun
**yayılım** dediği şeyin en yalın hâli: bir iç çağrının kendi sınırını mı açacağı, yoksa
dışarıdaki sınırın içinde mi kalacağı sorusu — burada yanıt üçüncü bir olasılık, iç çağrının
**hiçbir sınıra hiç girmemesi**, çünkü vekil onu hiç görmüyor.

#### İç Çağrı Vekili Hiç Görmüyor

- **WV27.** `sarmala` yalnız `Hesap` arayüzünü uygulayan bir vekil nesnesi döndürüyor;
  `HesapGerceklestirimi`'nin kendisi hiç değişmiyor, `this` hâlâ gerçek nesneyi gösteriyor.
- **WV28.** Vekilin araya girebilmesi için bir çağrının onun ürettiği nesne üzerinden
  geçmesi gerekiyor. `yatirVeBildir` içindeki `yatir(miktar)` çağrısı bu nesneyi hiç
  kullanmıyor.

Bunun nedeni, vekilin nasıl çalıştığında yatıyor. `Proxy.newProxyInstance` yeni bir nesne
üretiyor — `vekil` — ve bu nesne dışarıdan gelen her çağrıyı yakalayıp `sinirAcilan`'ı
artırdıktan sonra gerçek nesneye (`hedef`) yönlendiriyor. Ama `HesapGerceklestirimi` sınıfının
kendi kaynak kodu içinde `yatir(miktar)` yazıldığında, bu çağrı `hedef.yatir(miktar)` değil,
**gerçek nesnenin kendi `this`'i üzerinden** yapılan sıradan bir Java yöntem çağrısı. Nesnenin
kendisi hiçbir zaman "ben bir vekilin arkasındayım" bilgisine sahip değil — vekil, nesnenin
etrafına konan ayrı bir katman, nesnenin bir parçası değil. Bu yüzden `yatir`'in üzerindeki
`@Islem` açıklaması, bu iç çağrı için hiçbir zaman okunmuyor: onu okuyacak taraf yalnız
vekilin çağrı işleyicisi ve çağrı işleyicisi yalnız vekil üzerinden geçen çağrıları görüyor.

Bu, dersin sınırlayıcı ölçümü: **bir bileşenin kendi kendini çağırması vekili atlar ve
açıklama hiç uygulanmaz — çağrı içeriden geldiği için sarmalama devreye girmez, hata çıkmaz,
sınır açılmaz.** Yol B'nin günlüğü bunu doğruluyor: `yatir` ve `yatirVeBildir` toplam üç kez
çalıştı (dışarıdan iki, içeriden bir), ama günlükte yalnız iki `sinir-acildi` girdisi var. Ne
bir istisna fırlıyor ne bir uyarı basılıyor — program tamamen normal görünen bir yanıt
üretiyor, `bakiye=150; bildirildi`. Kaynak metni okuyan biri `yatir`'in üzerinde `@Islem`
gördüğü için onun her çağrıldığında bir sınır açacağını varsayabilir; bu varsayım
`yatirVeBildir` üzerinden gelen çağrılar için yanlış ve yanlışlığı gösteren tek şey sayaç,
hiçbir hata mesajı değil.

Bu davranışın kökü, vekilin kurulma biçiminde. `Kap.sarmala` yeni bir nesne **üretiyor**,
var olan nesneyi **değiştirmiyor** — `HesapGerceklestirimi`'nin kendi bayt kodunda hiçbir
satır eklenmedi, hiçbir yöntem araya giren bir çağrıyla değiştirilmedi. Vekil, `hedef`'i saran
ayrı bir sınıfın çalışma zamanında üretilen bir örneği ve bu örnek yalnız `Hesap` arayüzünü
uyguladığı için, arayüz üzerinden gelen çağrılar dışında hiçbir yola dahil değil. Bir
gerçekleştirim sınıfının içine yazılmış bir çağrı, o sınıfın kendi tanıdığı tek nesneyi
görüyor: kendisi. Vekilin varlığından habersiz olmak, gerçekleştirim sınıfının bir eksikliği
değil — vekil zaten sınıfın kendisine hiçbir iz bırakmadan, tamamen dışarıdan ekleniyor.

#### Elle Kurulan Nesnede Sınır Hiç Açılmıyor

Son ölçüm `sarmala` hiç çağrılmadan kurulan bir `HesapGerceklestirimi` nesnesiyle yapılıyor.
`elleKurulan.yatirVeBildir(100)` çağrısı normal şekilde tamamlanıyor, `bakiye=100;
bildirildi` döndürüyor — ama `sinirAcilan` sıfırda kalıyor. Bu, önceki derslerde defalarca
görülen kuralın işlem yönetimindeki karşılığı: `@Islem` açıklaması `HesapGerceklestirimi`
sınıfının üzerinde durmaya devam ediyor, ama onu okuyacak bir vekil hiç kurulmadıysa hiçbir
sınır hiçbir zaman açılmıyor. Yol B'deki iç çağrı ile bu elle kurulan nesne arasındaki fark
ince ama önemli: iç çağrıda vekil **var**, yalnız o çağrı için devreye girmiyor; elle kurulan
nesnede vekil **hiç yok**. İkisi de aynı sonucu — sınırın açılmaması — üretiyor, ama
biri nesne kabının kendi kuralından (yalnız dışarıdan gelen çağrılar sarmalanır), öteki kabın hiç
devrede olmamasından geliyor.

Bu üç kurulumun birlikte gösterdiği şey şu: bildirimsel bir sınırın var olduğunu bilmek,
onun **her zaman** çalışacağını bilmek anlamına gelmiyor. Sınırın açılması üç koşula birden
bağlı — açıklama sınıfın üzerinde olmalı, çağrı vekil üzerinden geçmeli ve çağrı dışarıdan
gelmeli. Üçü de sağlanmadığı sürece, kaynakta `@Islem` yazması hiçbir şey garanti etmiyor.

Bu üç koşulun hiçbiri kaynak metninde tek bir yerde toplu olarak yazılı değil. `@Islem`
açıklaması `HesapGerceklestirimi`'nin içinde, vekil kurulumu `main`'in içinde ve çağrının
dışarıdan mı içeriden mi geldiği yalnız çağrının **hangi değişken üzerinden** yapıldığına
bakılarak anlaşılabiliyor — `vekil.yatir(...)` mı, `this.yatir(...)`'e denk gelen çıplak
`yatir(...)` mu. Bu üç bilgiyi aynı anda tutmak, bir kod incelemesinin gözden
kaçırabileceği bir şey: `yatirVeBildir` yönteminin gövdesine bakan biri, `yatir` çağrısının
hangi nesne üzerinden yapıldığını fark etmeden geçebilir, çünkü Java söz dizimi `yatir(...)`
ile `this.yatir(...)`'i ayırt etmiyor — ikisi aynı çağrı, aynı görünüm.

#### Özet

- Yalnız `yatirVeBildir`'in dışarıdan çağrıldığı yolda sınır bir kez açıldı; `yatir` ile
  `yatirVeBildir`'in ayrı ayrı dışarıdan çağrıldığı yolda sınır iki kez açıldı.
- `yatirVeBildir` içindeki `yatir(miktar)` iç çağrısı hiçbir kurulumda sınır sayısına
  eklenmedi; vekil yalnız dışarıdan gelen çağrıları görüyor.
- İç çağrı `this` üzerinden yapıldığı için vekili hiç görmedi; `@Islem` açıklaması sınıfın
  üzerinde durmaya devam etse de bu çağrı için hiç okunmadı.
- Kusur sessizdi: iç çağrı ne bir istisna fırlattı ne bir uyarı bastı, yalnız beklenen
  sınırı açmadan tamamlandı.
- Vekil hiç kurulmadan çağrılan aynı sınıfta sınır bir kez bile açılmadı; sınırın
  çalışması açıklamanın varlığına değil, üç koşulun birlikte sağlanmasına bağlı.

#### Sonraki Adım

Bu konu burada kapanıyor. Bu ders sınırın nerede açıldığını bir açıklamanın söylediğini ve
içeriden gelen bir çağrının o sınırı hiç görmediğini gösterdi. Sıradaki konu aynı soruyu
başka bir yerde soruyor: güvenlik, test ve dağıtık kurulumda da bir sınır — bu kez bir yetki
sınırı — yine bir açıklamayla çiziliyor ve içeriden gelen bir çağrının onu görüp görmediği
yeniden ölçülüyor.

## Güvenlik, Test ve Dağıtık Kurulum

Kimlik doğrulama ve yetkilendirmenin filtre zinciriyle uygulanması, durumsuz oturumda belirtecin taşıdığı ve taşımadığı bilgi, katman testlerinin kabın hangi parçasını ayağa kaldırdığı, hiç yazılmayan sağlık ve ölçüm uçları, yapılandırmanın merkezîleşmesi ve yenilenmesi, servis keşfiyle ağ geçidinin yönlendirmeyi devralması, dayanıklılık kalıplarının açıklamayla eklenmesi.

### Kimlik Doğrulama ve Yetkilendirme

Source: https://academia.sh/tr/kurslar/kurumsal-uygulama-cercevesi/kimlik-dogrulama-ve-yetkilendirme

Yetkilendirme kararı bir açıklamadan mı geliyor, kabın varsayılanından mı, yoksa kaynakta yazılı bir denetim ifadesinden mi — filtre zinciri modellenerek üçü ayrılır; hiçbir kural taşımayan bir uç noktanın açık mı kapalı mı olduğunun, kabın kurulması sırasında verilen tek bir parametreyle belirlendiği ve bu farkın kaynakta hiç görünmediği koşturularak gösterilir.

Önceki konunun son dersi işlem sınırının kaynakta nasıl açıldığını ölçtü: sınırı bir açıklama
koyuyordu ve bileşenin kendi içinden yaptığı çağrı bu sınırı hiç görmüyordu — vekil aradan çıkınca
açıklama da aradan çıkıyordu. Bu konu aynı soruyu güvenlik, test ve dağıtık kurulum için soruyor;
ilk durak yetkilendirme. Bir uç noktaya kimin erişebileceğini kaynakta arayan biri çoğu zaman
hiçbir şey bulmaz: ne bir denetim satırı, ne bir rol karşılaştırması. Karar ya bir açıklamadan
gelir ya da hiçbir yerden — nesne kabının kendi varsayılanından. Kimlik doğrulamanın ve
yetkilendirmenin kavramları — kimin kim olduğu, hangi yetkinin nasıl temsil edildiği, rol ile izin
arasındaki fark — M16/K03 Kimlik Doğrulama ve Yetkilendirme ile M24/K04 Kimlik, Erişim ve
Kriptografi kurslarında kuruldu ve burada tekrarlanmıyor; bu ders yalnız kararın hangi düzenekle
verildiğini ölçüyor. K06'nın sorduğu soru bu konuda da değişmiyor: bu davranışı kaynakta bir çağrı
mı, bir açıklama mı doğurdu, yoksa hiçbiri mi? Bir filtre zinciri modelleniyor ve üç kaynak
ayrılıyor: çağrı, açıklama, varsayılan. En tehlikelisi üçüncüsü, çünkü kabın varsayılanı kaynakta
hiçbir iz bırakmıyor; aynı kaynak metni iki farklı varsayılanla iki farklı güvenlik duruşu veriyor
ve bu ders bunu koşturarak gösteriyor.

Bu dersin sınırları da baştan çiziliyor: kimlik doğrulama (çağıranın kim olduğunu kanıtlaması) ile
yetkilendirme (kanıtlanmış kimliğin neyi yapabileceğine karar verilmesi) arasındaki ayrım
M16/K03'te kuruldu; bu ders çağıranın kimliğini zaten çözülmüş bir rol dizesi olarak alıyor ve
yalnız yetkilendirme kararının kaynağını ölçüyor — kimliğin nasıl kanıtlandığı sıradaki dersin
konusu. Gerçek bir soket, gerçek bir istek gövdesi ve gerçek bir oturum deposu yok; filtre zinciri
ve kararı okuyan nesne kabı tek başına modelleniyor.

#### Filtre Zincirinin Uyguladığı Karar

Modellenen nesne kabı, `@UcNokta` işaretli yöntemleri bir yol adına eşleyen küçük bir sınıf;
bu, framework-core konusundaki nesne kabıyla aynı düzenek — orada nesneyi kuran açıklamalar
ölçüldü, burada aynı kabın bir isteği hangi kurala göre geçirdiği ölçülüyor. `Bekci` kurulurken
hedef nesnenin bütün yöntemlerini bir kez tarıyor ve `@UcNokta` taşıyanları bir yol adına
eşliyor; bu tarama kuruluş anında bir kez oluyor, her çağrıda yeniden yapılmıyor. Çağrı
geldiğinde tek halkalı bir filtre zinciri devreye giriyor: yöntemde `@Yetki` işareti varsa
çağıranın rolünü işaretteki rolle karşılaştırıyor; işaret yoksa kabın kurulurken verilen
`varsayilanAcikMi` alanına bakıyor. Gerçek bir ağ isteği, gerçek bir oturum ya da gerçek bir
sunucu yok — zincirin kendisi ve kararı okuyan yansıma var.

```java
// Bekci.java — istegi filtre zincirinden gecirip yetki kararini kaynaktaki isarete ya da kabin varsayilanina gore veren kucuk bir nesne kabi (yardimci kaynak, main yok)
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.util.*;

@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD)
@interface UcNokta { String yol(); }

@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD)
@interface Yetki { String rol(); }

class Bekci {
    final Object hedef;
    final boolean varsayilanAcikMi;
    final Map<String, Method> ucNoktalar = new LinkedHashMap<>();

    Bekci(Object hedef, boolean varsayilanAcikMi) {
        this.hedef = hedef;
        this.varsayilanAcikMi = varsayilanAcikMi;
        for (Method m : hedef.getClass().getDeclaredMethods()) {
            UcNokta u = m.getAnnotation(UcNokta.class);
            if (u != null) ucNoktalar.put(u.yol(), m);
        }
    }

    // filtre zincirinin tek halkasi: aciklama varsa onu okur, yoksa kabin varsayilanina doner.
    boolean izinVer(String yol, String cagiranRolu) {
        Method m = ucNoktalar.get(yol);
        if (m == null) return false;
        Yetki y = m.getAnnotation(Yetki.class);
        if (y == null) return varsayilanAcikMi;
        if (y.rol().equals("herkes")) return true;
        return y.rol().equals(cagiranRolu);
    }

    String cagir(String yol, String cagiranRolu) throws Exception {
        if (!izinVer(yol, cagiranRolu)) return "engellendi";
        Method m = ucNoktalar.get(yol);
        m.setAccessible(true);
        return (String) m.invoke(hedef);
    }

    static void gozle(String davranis, String kaynak, Object sonuc) {
        System.out.printf("%-40s %-11s: %s%n", davranis, kaynak, sonuc);
    }
}

class Uclar {
    @UcNokta(yol = "/panel") @Yetki(rol = "yonetici")
    String panel() { return "panel-verisi"; }

    @UcNokta(yol = "/durum")
    String durum() { return "calisiyor"; }

    @UcNokta(yol = "/surum") @Yetki(rol = "herkes")
    String surum() { return "surum-3"; }
}
```

`Uclar` sınıfının kaynağında `izinVer` yöntemini çağıran, ya da rolleri karşılaştıran hiçbir
satır yok. `panel` yönteminin üzerinde duran `@Yetki(rol = "yonetici")` yalnız bir işaret;
onu okuyan ve uygulayan taraf `Bekci`. Açıklama tipleri `@Retention(RetentionPolicy.RUNTIME)`
ile işaretli, çünkü kararı derleyici değil çalışma zamanında yansıma veriyor — derleme
sınıf yolundan silinen bir açıklama, `Bekci`'nin okuyacağı hiçbir şey bırakmaz; hedef
`ElementType.METHOD` olduğu için işaret yalnız yöntemlere konabiliyor, sınıfın kendisine ya
da alanlarına taşınamıyor. **GT1.** Filtre zincirinin tek halkası yetki açıklamasını okuyup
çağıranın rolüyle karşılaştırır; açıklama yoksa kabın kendi varsayılanına döner. **GT2.**
Yetki açıklaması taşıyan bir uç noktada kararı kaynakta hiçbir çağrı ifadesi doğurmaz — karar
açıklamadan gelir.

```java
// FiltreKarari.java — yetki aciklamasi olan bir uc noktada karar, cagiranin rolunu aciklamayla karsilastiran filtre zincirinden geliyor
public class FiltreKarari {
    public static void main(String[] args) throws Exception {
        Bekci bekci = new Bekci(new Uclar(), true);
        Bekci.gozle("panel, cagiran yonetici", "aciklama", bekci.cagir("/panel", "yonetici"));
        Bekci.gozle("panel, cagiran calisan", "aciklama", bekci.cagir("/panel", "calisan"));
    }
}
```

```
panel, cagiran yonetici                  aciklama   : panel-verisi
panel, cagiran calisan                   aciklama   : engellendi
```

Aynı `bekci` nesnesi, aynı `cagir` çağrısı, aynı `/panel` yolu — değişen tek şey argüman
olarak verilen rol. Yönetici rolüyle gelen çağrı yönteme ulaşıyor ve gövdesini döndürüyor;
çalışan rolüyle gelen çağrı yöntemi hiç çalıştırmadan `engellendi` sonucunu alıyor. Karar
`panel` yönteminin gövdesinde değil, üzerindeki işarette duruyor ve işareti okuyan taraf
`Bekci`'nin kendisi; `panel` yöntemi kendi başına ne yönetici sözcüğünü tanır ne kimin
çağırdığını sorar. Yöntem yeniden yazılmadan, üzerindeki işaret değiştirilerek — örneğin
`rol = "calisan"` yapılarak — aynı davranış farklı bir role açılabilir; bu değişiklik gövdeye
hiç dokunmaz.

Yol eşlemesi `Bekci` kurulurken bir kez çıkarılıyor; `cagir` her seferinde yeniden yansıma
taraması yapmıyor, önceden çıkarılmış `ucNoktalar` haritasına bakıyor. Tanınmayan bir yol
istendiğinde `izinVer` doğrudan `false` döner — bu da bir çağrı değil, kabın kendi kuralı:
haritada karşılığı olmayan her yol, hiçbir açıklama okunmadan reddedilir. Yani üçüncü bir
varsayılan daha var, ama bu kez yönü sabit: bilinmeyen yol her koşulda kapalı, `varsayilanAcikMi`
alanından bağımsız. Bilinen ile bilinmeyen yol arasındaki bu asimetri kaynakta da görünmüyor;
yalnızca `ucNoktalar` haritasının içeriğine bakarak anlaşılabiliyor.

#### Kural Yazılmadığında: Varsayılanın Yönü

`Uclar` sınıfındaki `durum` yönteminin üzerinde hiçbir yetki işareti yok. Bu, kaynağı okuyan
birinin hiçbir şey bulamayacağı durum — ve tam da bu yüzden en sınırlayıcı ölçüm burada.
`izinVer` yöntemi işaret bulamadığında `varsayilanAcikMi` alanına bakıyor; bu alan `Bekci`
kurulurken verilen bir kurucu parametresi, kaynağın hiçbir yerinde görünmeyen bir değer.
İki kurulum arasındaki fark yalnız bu tek `boolean` değeri: hiçbir sınıf yeniden yazılmıyor,
hiçbir açıklama eklenmiyor.

```java
// VarsayilanYonu.java — kaynagi degismeyen bir uc nokta, kabin kurulurken verilen tek bir parametreyle iki ayri sonuc uretiyor
public class VarsayilanYonu {
    public static void main(String[] args) throws Exception {
        Bekci acikBekci = new Bekci(new Uclar(), true);
        Bekci kapaliBekci = new Bekci(new Uclar(), false);

        Bekci.gozle("durum, acik-kurulumda", "varsayilan", acikBekci.cagir("/durum", "misafir"));
        Bekci.gozle("durum, kapali-kurulumda", "varsayilan", kapaliBekci.cagir("/durum", "misafir"));
        System.out.println("kaynak birebir ayni: Uclar.durum() hicbir yetki isareti tasimiyor");
    }
}
```

```
durum, acik-kurulumda                    varsayilan : calisiyor
durum, kapali-kurulumda                  varsayilan : engellendi
kaynak birebir ayni: Uclar.durum() hicbir yetki isareti tasimiyor
```

`Uclar.java` dosyası iki koşum arasında tek bir karakter değişmedi; `acikBekci` ile
`kapaliBekci` aynı sınıftan kuruldu. Buna rağmen `misafir` rolüyle yapılan aynı çağrı bir
kurulumda veriyi döndürüyor, ötekinde reddediliyor. **GT3.** Yetki açıklaması taşımayan bir
uç noktada karar kabın kendi varsayılanından gelir; kaynak metni bu iki yönden hiçbirini
göstermez. **GT4.** Aynı kaynak, yeniden derlenmeden, yalnız kabın kurulması sırasında
verilen bir kurucu parametresiyle iki farklı güvenlik duruşu üretebilir — bu, dersin
sınırlayıcı ölçümü. İşaretlenmemiş bir uç nokta güvenli değildir ve güvensiz de değildir;
hangisi olduğu, kaynağı okuyarak değil, kabın hangi varsayılanla kurulduğunu bilerek
anlaşılır. Bu, dersin en önemli gözlemi: iki uç nokta arasındaki farkı "açık" ile "kapalı"
gibi sabit bir sıfatla anmak yanıltıcı, çünkü sıfat kaynağa değil kurulum kararına ait.

Bu karar `Uclar` sınıfını yazan tarafa da ait değil; `Bekci`'yi kim kuruyorsa varsayılan onun
elinde. Çok sayıda uç noktanın bulunduğu bir sınıfta yetki açıklaması unutulan tek bir yöntem,
varsayılan açık bir kurulumda sessizce erişilebilir kalır — kaynakta hiçbir hata, hiçbir
uyarı yok, çünkü unutulan bir işaret zaten hiçbir iz bırakmaz. Varsayılan kapalı bir kurulumda
aynı unutkanlık tam tersi yönde çalışır: işaretlenmemiş her yöntem erişilmez kalır ve
eksikliği ancak çağıran tarafın "beklenmedik biçimde engellendi" gözlemiyle fark edilir. İki
yön de aynı boşluğu dolduruyor, ama doldurduğu sonuç taban tabana zıt; hangi yönün seçildiği
kabı kuran satırda bir kez yazılıyor ve bütün işaretlenmemiş yüzeyi aynı anda kapsıyor.

Varsayılanın yönü kaynakta görünmüyorsa, açıklama onu bastırabilir mi? `surum`
yönteminin üzerindeki `@Yetki(rol = "herkes")` tam bunu deniyor: özel bir rol adı değil, bir
açık kapı işareti.

```java
// AcikcaBelirtilmis.java — herkese-acik isareti tasiyan bir uc nokta, kabin varsayilani kapali olsa bile aciklamadan geciyor
public class AcikcaBelirtilmis {
    public static void main(String[] args) throws Exception {
        Bekci kapaliBekci = new Bekci(new Uclar(), false);
        Bekci.gozle("surum, kapali-kurulumda", "aciklama", kapaliBekci.cagir("/surum", "misafir"));
        Bekci.gozle("durum, kapali-kurulumda", "varsayilan", kapaliBekci.cagir("/durum", "misafir"));
    }
}
```

```
surum, kapali-kurulumda                  aciklama   : surum-3
durum, kapali-kurulumda                  varsayilan : engellendi
```

Aynı `kapaliBekci` nesnesi üzerinde iki çağrı: `surum` açıklamayla herkese açık ilan edildiği
için varsayılanı bastırıp geçiyor; `durum` hiçbir işaret taşımadığı için aynı kabın
kapalı varsayılanına düşüyor. Açıklama varsa, varsayılan hiç devreye girmiyor — sıra ancak
açıklama yoksa varsayılana geliyor. Bu, iki kaynağın öncelik sırasını gösteriyor: önce
açıklama okunuyor, o yoksa varsayılana dönülüyor; kaynakta yazılı bir kural varsayılanın
yönünden bağımsız olarak kazanıyor. Bu öncelik sırası da kaynakta değil `izinVer`
yönteminin kendi gövdesinde duruyor — `Bekci`'yi yazan taraf hangi kaynağın önce
sorulacağını baştan kararlaştırmış.

#### Elle Yazılan Yetki Denetimi

Üçüncü kaynak, önceki ikisinden ayrı bir sınıfta duruyor: `ElleYetkili`, filtre zincirine hiç
girmeyen, `@UcNokta` ya da `@Yetki` taşımayan sıradan bir yöntem. Kararı burada nesne kabı
değil, yöntemin kendi gövdesindeki bir denetim ifadesi veriyor.

```java
// ElleYetkili.java — yetki karari nesne kabi ya da aciklama uzerinden degil, dogrudan kaynaktaki bir denetim ifadesinden geliyor (yardimci kaynak, main yok)
class ElleYetkili {
    String hassasIslem(String cagiranRolu) {
        if (!"yonetici".equals(cagiranRolu)) throw new SecurityException("yetkisiz erisim");
        return "hassas-veri";
    }

    String dene(String cagiranRolu) {
        try { return hassasIslem(cagiranRolu); }
        catch (SecurityException e) { return e.getClass().getSimpleName(); }
    }
}
```

```java
// ElleKontrol.java — ayni iki rolle cagrildiginda, kararin kaynaktaki bir kosuldan geldigini gosteriyor
public class ElleKontrol {
    public static void main(String[] args) {
        ElleYetkili elle = new ElleYetkili();
        Bekci.gozle("hassasIslem, cagiran yonetici", "cagri", elle.dene("yonetici"));
        Bekci.gozle("hassasIslem, cagiran calisan", "cagri", elle.dene("calisan"));
    }
}
```

```
hassasIslem, cagiran yonetici            cagri      : hassas-veri
hassasIslem, cagiran calisan             cagri      : SecurityException
```

Sonuç `FiltreKarari` dersindekiyle aynı görünüyor — yönetici geçiyor, çalışan reddediliyor —
ama kaynağı farklı. **GT5.** Elle yazılmış bir denetim ifadesi kararı doğrudan kaynaktan
verir; ne bir açıklama ne nesne kabı araya girer, karar `hassasIslem` yönteminin ilk
satırında duran karşılaştırmadan gelir. Bu üçüncü yol, `Bekci`'nin hiç yönetmediği bir
sınıfta yaşıyor; `ElleYetkili` nesnesi `Bekci`'ye hiç kaydolmadı, dolayısıyla ne yetki
açıklaması ne varsayılan yön ona uygulanabilir — bir önceki konunun kapanış dersindeki
gözlemle aynı sınır burada da geçerli: kabın dışında kalan nesne, kabın sunduğu hiçbir
düzeneği almaz.

Üç dersin de gösterdiği üç ayrı kaynak burada yan yana duruyor:

| Kaynak | Kararı veren yer | Bu derste örneği |
|---|---|---|
| açıklama | `@Yetki` işareti, `Bekci` tarafından okunur | `panel`, `surum` |
| varsayılan | `Bekci` kurulurken verilen `varsayilanAcikMi` | `durum` |
| çağrı | yöntemin kendi gövdesindeki denetim ifadesi | `hassasIslem` |

Aynı "yönetici mi değil mi" sorusu üç ayrı yerden üç ayrı biçimde yanıtlanıyor ve kaynağı
yalnızca okuyan biri — açıklamaları görse bile — hangi yanıtın hangi davranışı ürettiğini
bilmeden yola çıkıyor. `durum` özelinde bu bilgisizlik daha da keskin: orada okunacak hiçbir
işaret yok, karar tümüyle kabın kuruluş anına ait. Bu üç kaynağın hiçbiri "doğru" ya da
"yanlış" değil; her biri farklı bir yerde tutuluyor ve kaynağı okuyan kişinin hangi yerlere
bakması gerektiğini önceden bilmesi gerekiyor — kurs boyunca kurulan ölçüm ekseni tam olarak
bu bilme ihtiyacını sayılabilir kılmak için var.

Üç kaynağın ayrımı bir de büyüklükle ilgili: açıklama ve varsayılan bütün bir sınıfı ya da
bütün bir kabı tek bir kararla kapsıyor, elle yazılan denetim ise yalnız yazıldığı yöntemi.
`hassasIslem` gibi on ayrı yöntem olsaydı, aynı karşılaştırma on kez tekrarlanırdı; işaret
tabanlı yaklaşım bu tekrarı ortadan kaldırıyor ama karşılığında kararı kaynaktan kaba
taşıyor. Bu ders çağıranın kimliğini zaten çözülmüş bir rol dizesi olarak aldı — `"yonetici"`,
`"calisan"`, `"misafir"` — ve bu dizenin nereden geldiğini hiç sormadı.

#### Özet

- Filtre zinciri, yetki açıklaması olan bir uç noktada kararı işaretten okur ve çağıranın
  rolüyle karşılaştırır; bu davranışın kaynağı açıklamadır.
- Yetki açıklaması olmayan bir uç noktada karar kabın kendi varsayılanından gelir; kaynak
  metni bu davranışı hiç göstermez.
- Aynı kaynak, yeniden derlenmeden, yalnız kabın kurulması sırasında verilen bir parametreyle
  iki farklı güvenlik duruşu üretebilir — sınırlayıcı ölçüm budur.
- Açıklama varsa varsayılan hiç devreye girmez; öncelik sırası önce açıklama, sonra
  varsayılandır.
- Elle yazılmış bir denetim ifadesi kararı doğrudan kaynaktan verir; bu yöntem kaba hiç
  kaydolmadığından ne açıklama ne varsayılan yön ona uygulanır.

#### Sonraki Adım

Bu ders çağıranın rolünü hazır bir dize olarak aldı — kim olduğu zaten bilinen bir girdiydi.
Gerçek bir çağıran bu bilgiyi bir yerden taşımak zorunda ve bu kurs sunucu tarafında bir
oturum tutmuyor. Sıradaki ders bu rolün ve kimliğin sunucuda saklanan bir durumdan değil,
çağıranın her istekte yanında getirdiği bir belirteçten nasıl geldiğini modelliyor.

### Belirteç Tabanlı Kimlik Doğrulama

Source: https://academia.sh/tr/kurslar/kurumsal-uygulama-cercevesi/belirtec-tabanli-kimlik-dogrulama

Durumsuz oturumda kimliğe dair her şey belirtecin taşıdığı iddialarda durur; sunucu tarafında hiçbir oturum kaydı yok. Bir yetki sorusunun bir açıklamadan mı yoksa kaynaktaki bir karşılaştırmadan mı yanıtlandığı koşturularak ayrılır; sınırlayıcı ölçüm, iptal edilmiş bir belirtecin doğrulayıcı hiçbir iptal listesine bakmadığı için süresi dolana kadar kaç adım geçerli sayılmaya devam ettiğini sayıyla gösterir.

Önceki ders çağıranın rolünü hazır bir dize olarak aldı ve bu dizenin nereden geldiğini hiç
sormadı. Bu ders tam o soruyu soruyor. Bir sunucu, bir çağıranın kim olduğunu iki yoldan
birinden bilebilir: ya her istekte o çağırana ait bir kaydı kendi tarafında tutar, ya da
kimliğe dair her şeyi çağıranın taşıdığı bir belirteçte saklar ve kendi tarafında hiçbir şey
tutmaz. İkincisi **durumsuz oturum**: sunucunun belleğinde çağıranla ilgili sıfır kayıt var,
her isteğin kimliği o isteğin kendi içinde geliyor. Bu ders belirteci modelliyor — gerçek bir
anahtar, gerçek bir imza algoritması ya da gerçek bir kriptografi yordamı yazmadan — ve iki
şeyi ölçüyor: belirtecin taşıdığı iddia (belirteç) sayısı ve bir yetki sorusunun yanıtının kaynakta bir
açıklamadan mı yoksa bir çağrıdan mı geldiği. Sınırlayıcı ölçüm en sona bırakılıyor, çünkü
durumsuzluğun bir bedeli var ve bu bedel ancak belirtecin ömrü boyunca koşturularak görülüyor.

Oturum tabanlı bir yaklaşımda sunucu her çağıran için bir kayıt tutar; kaydı silmek çağıranı
anında geçersiz kılar, çünkü her istek zaten o kayda bakar. Durumsuz yaklaşım bu bakışı hiç
yapmaz — kimliğin kanıtı her istekle birlikte gelir, sunucu tarafında sorgulanacak bir kayıt
yoktur. Bu fark yalnız bir uygulama ayrıntısı değil, bu dersin ölçtüğü şeyin tam kendisi.

#### Belirtecin Taşıdığı İddialar

Modellenen belirteç iki alan taşıyor: bir konu (çağıranın kimliği) ve bir iddialar haritası
— konu hakkında taşınan her bilgi bu haritada bir anahtar-değer çifti. Üçüncü alan bir
geçerlilik sonu, ama bu gerçek bir zaman damgası değil: dersin ilerleyen bölümünde
kullanılacak, adım sayan bir tam sayı. Gerçek saat, gerçek süre ya da gerçek bir imza yok;
belirtecin kendisi sıradan bir veri taşıyıcısı olarak modelleniyor.

```java
// Belirtec.java — kimlige dair her seyi tasiyan durumsuz veri tasiyicisi; gecerlilikSonu gercek zaman degil, adim sayan bir tam sayi (yardimci kaynak, main yok)
import java.util.*;

record Belirtec(String konu, Map<String, String> iddialar, int gecerlilikSonu) { }

class Dogrulayici {
    // durumsuz dogrulama: yalniz belirtecin kendi alanlarina ve verilen adima bakar,
    // hicbir paylasilan duruma (iptal listesi, oturum deposu) erismiyor.
    static boolean gecerliMi(Belirtec b, int suAn) {
        return suAn <= b.gecerlilikSonu();
    }

    static void goster(String etiket, Object deger) {
        System.out.printf("%-42s: %s%n", etiket, deger);
    }
}
```

```java
// IddiaSayisi.java — iki belirtecin tasidigi iddia sayisi sayiliyor; sunucu tarafinda tutulan oturum kaydi sayisi sifir
import java.util.*;

public class IddiaSayisi {
    public static void main(String[] args) {
        Belirtec misafir = new Belirtec("kullanici-7", Map.of("rol", "misafir"), 10);
        Belirtec yonetici = new Belirtec("kullanici-2",
                Map.of("rol", "yonetici", "departman", "IT", "seviye", "3"), 10);

        Dogrulayici.goster("misafir belirteci, tasidigi iddia sayisi", misafir.iddialar().size());
        Dogrulayici.goster("yonetici belirteci, tasidigi iddia sayisi", yonetici.iddialar().size());
        Dogrulayici.goster("sunucu tarafinda tutulan oturum kaydi sayisi", 0);
    }
}
```

```
misafir belirteci, tasidigi iddia sayisi  : 1
yonetici belirteci, tasidigi iddia sayisi : 3
sunucu tarafinda tutulan oturum kaydi sayisi: 0
```

`misafir` bir iddia taşıyor, `yonetici` üç. Bu sayı sabit değil — her belirteç kendi
taşıdığı bilgi kadar ağır ve sunucu tarafında bu bilgiyi tekrar tutan hiçbir yapı yok.
**GT6.** Belirteç, sunucu tarafında hiçbir oturum kaydı tutulmadan, kimliğe dair bütün
bilgiyi kendi iddiaları içinde taşır; bu, durumsuz oturumun tanımıdır. **GT7.** Taşınan
iddia sayısı belirteçten belirtece değişebilir, ama sunucu tarafında tutulan kayıt sayısı
her zaman sıfırdır — önceki derste ölçülen `Bekci`'nin `ucNoktalar` haritası gibi bir yapı
burada yok, çünkü ölçülecek bir çağıran durumu zaten bulunmuyor.

Gerçek bir belirteç sistemi iddiaları bir imzayla korur — belirtecin içeriği değiştirilmiş
mi diye bakan bir doğrulama adımı. Bu ders o adımı yazmıyor; imza, anahtar ve kriptografi
yordamları M24/K04 Kimlik, Erişim ve Kriptografi kursunda kuruldu ve burada tekrarlanmıyor.
Burada `Belirtec` sıradan bir kayıt (`record`), imzasız ve doğrudan üretilmiş; ölçülen şey
imzanın kendisi değil, imza doğru olduğunda bile geriye kalan iki soru — iddia ne kadar bilgi
taşıyor ve o bilgi nasıl okunuyor.

Durumsuzluğun bu konudaki asıl kazancı, kimliği doğrulayan tarafın hiçbir paylaşılan kayda
ihtiyaç duymaması. Bir çağrıyı hangi bileşenin karşıladığı önemsiz hâle geliyor, çünkü her
bileşen belirteci tek başına, kendi elindeki bilgiyle doğrulayabiliyor — bir öncekinin
tuttuğu bir kaydı sormasına gerek yok. Bu özellik ilerideki iki derste tekrar karşımıza
çıkacak: dağıtık yapılandırma dersinde birden çok bileşenin aynı değeri görüp görmediği
sorgulanacak, servis keşfi dersinde bir çağrının hangi bileşene ulaştığı hiç sabit
olmayacak. Durumsuz kimlik, bu değişkenliğin bir sorun çıkarmamasının nedenlerinden biri.

#### Doğrulamanın Kaynağı: Açıklama mı Çağrı mı

Bir belirtecin taşıdığı iddiaların doğru olup olmadığı ayrı bir soru; bu ders yalnız
iddiaların **okunma** biçimini ölçüyor — "rol iddiası yönetici mi" sorusunun yanıtı nereden
geliyor? İki yol modelleniyor. Birincisi bir açıklama: yönteme konan `@GerekliIddia` işareti,
gerekli anahtarı ve beklenen değeri taşıyor, onu okuyan ve karşılaştıran taraf çağıran değil,
denetim sınıfının kendisi.

```java
// GerekliIddia.java — bir yontemin gecmesi icin belirtecte hangi iddianin hangi degeri tasimasi gerektigini belirten aciklama tipi (yardimci kaynak, main yok)
import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD)
@interface GerekliIddia { String ad(); String deger(); }
```

```java
// IddiaDenetimi.java — aciklamayi okuyup belirtecin ilgili iddiasiyla karsilastiran denetim (yardimci kaynak, main yok)
import java.lang.reflect.*;

class IddiaDenetimi {
    static boolean karsilarMi(Method m, Belirtec b) {
        GerekliIddia g = m.getAnnotation(GerekliIddia.class);
        if (g == null) return true;
        return g.deger().equals(b.iddialar().get(g.ad()));
    }
}
```

```java
// GizliUclar.java — panel yontemi uzerindeki isaret, hangi iddianin gerekli oldugunu soyluyor (yardimci kaynak, main yok)
class GizliUclar {
    @GerekliIddia(ad = "rol", deger = "yonetici")
    String panel() { return "panel-verisi"; }
}
```

İkinci yol kaynakta yazılı bir karşılaştırma; hiçbir işaret okunmuyor, hiçbir yansıma
çalışmıyor.

```java
// ElleDogrulama.java — ayni soru, kaynakta yazili bir karsilastirmadan yanitlaniyor (yardimci kaynak, main yok)
class ElleDogrulama {
    static boolean yoneticiMi(Belirtec b) {
        return "yonetici".equals(b.iddialar().get("rol"));
    }
}
```

```java
// DogrulamaKaynagi.java — ayni iki belirtecle, aciklama temelli ve cagri temelli denetim ayni sonucu ureterek karsilastiriliyor
import java.lang.reflect.*;
import java.util.*;

public class DogrulamaKaynagi {
    public static void main(String[] args) throws Exception {
        Belirtec misafir = new Belirtec("kullanici-7", Map.of("rol", "misafir"), 10);
        Belirtec yonetici = new Belirtec("kullanici-2", Map.of("rol", "yonetici"), 10);

        Method panel = GizliUclar.class.getDeclaredMethod("panel");

        Dogrulayici.goster("aciklama: panel, misafir belirteci", IddiaDenetimi.karsilarMi(panel, misafir));
        Dogrulayici.goster("aciklama: panel, yonetici belirteci", IddiaDenetimi.karsilarMi(panel, yonetici));
        Dogrulayici.goster("cagri: elleDogrulama, misafir belirteci", ElleDogrulama.yoneticiMi(misafir));
        Dogrulayici.goster("cagri: elleDogrulama, yonetici belirteci", ElleDogrulama.yoneticiMi(yonetici));
    }
}
```

```
aciklama: panel, misafir belirteci        : false
aciklama: panel, yonetici belirteci       : true
cagri: elleDogrulama, misafir belirteci   : false
cagri: elleDogrulama, yonetici belirteci  : true
```

Dört satırın sonucu ikişer ikişer birebir aynı; `IddiaDenetimi.karsilarMi` ile
`ElleDogrulama.yoneticiMi` aynı iki belirteç için aynı iki yanıtı veriyor. **GT8.** Aynı
"rol yönetici mi" sorusu bir açıklamadan okunarak yanıtlanabiliyor: `panel` yönteminin
kaynağında ne "rol" sözcüğü ne bir karşılaştırma geçiyor, karar `@GerekliIddia` işaretinde
duruyor. **GT9.** Aynı soru kaynakta yazılı bir karşılaştırmadan da yanıtlanabiliyor:
`yoneticiMi` yönteminin ilk satırı `iddialar().get("rol")` çağrısını ve karşılaştırmayı
doğrudan taşıyor. İki kaynak aynı sonucu üretiyor, ama `GizliUclar` sınıfının kaynağını
okuyan biri "yönetici" sözcüğünü hiç görmezken, `ElleDogrulama` sınıfının kaynağını okuyan
biri onu ilk satırda buluyor.

`IddiaDenetimi.karsilarMi` yönteminin bir de üçüncü hâli var: `@GerekliIddia` işareti hiç
yoksa yöntem doğrudan `true` döner — önceki dersteki varsayılan-açık kurulumun aynısı, burada
tek satırlık bir kısayolla. Bu, açıklama ile çağrı arasındaki asıl farkı gösteriyor: açıklama
tabanlı denetim bütün `GizliUclar` sınıfı için tek bir yerde tanımlı, yeni bir yöntem
eklendiğinde default olarak izin veriyor ve gerekiyorsa üzerine işaret konuyor. Çağrı tabanlı
denetimde ise her yeni yöntem kendi karşılaştırmasını kendi yazmak zorunda; bir yöntem bu
satırı unutursa, unutulduğu hiçbir yerde görünmez — kaynak eksiksiz derlenir, çalışır ve
denetimsiz kalır.

#### Durumsuzluğun Bedeli: İptal Edilmiş Bir Belirtecin Ömrü

`Dogrulayici.gecerliMi` yalnız iki şeye bakıyor: belirtecin kendi `gecerlilikSonu` alanına
ve kendisine verilen `suAn` değerine. Bu, dersin en sınırlayıcı ölçümünü doğuruyor: bir
belirteç "iptal edilse" bile, doğrulayıcının bakacağı hiçbir iptal listesi yoksa, iptal
kaydı belirtecin geçerliliğini hiç etkilemiyor. Uygulama kendi tarafında bir iptal listesi
tutabilir, ama `gecerliMi` bu listeye hiç erişmiyor — erişseydi, durumsuz olmaktan çıkar,
her çağrıda paylaşılan bir yapıya bakan bir doğrulayıcıya dönerdi.

```java
// IptalVeGecerlilik.java — iptal edilen belirtec, dogrulayici iptal listesine hic bakmadigi icin suresi dolana kadar gecerli sayilmaya devam ediyor
import java.util.*;

public class IptalVeGecerlilik {
    public static void main(String[] args) {
        Belirtec belirtec = new Belirtec("kullanici-9", Map.of("rol", "calisan"), 10);
        Set<String> iptalEdilenler = new HashSet<>();

        Dogrulayici.goster("iptalden once, an=0, gecerli mi", Dogrulayici.gecerliMi(belirtec, 0));

        iptalEdilenler.add(belirtec.konu());
        Dogrulayici.goster("konu iptal listesinde mi", iptalEdilenler.contains(belirtec.konu()));

        int gecerliKalanAdim = 0;
        for (int an = 1; an <= 15; an++) {
            if (Dogrulayici.gecerliMi(belirtec, an)) gecerliKalanAdim++;
        }
        Dogrulayici.goster("iptalden sonra dogrulayicinin gecerli saydigi adim sayisi", gecerliKalanAdim);
        Dogrulayici.goster("an=11, gecerli mi (sure doldu)", Dogrulayici.gecerliMi(belirtec, 11));
    }
}
```

```
iptalden once, an=0, gecerli mi           : true
konu iptal listesinde mi                  : true
iptalden sonra dogrulayicinin gecerli saydigi adim sayisi: 10
an=11, gecerli mi (sure doldu)            : false
```

`iptalEdilenler` kümesi `belirtec.konu()`'yu tutuyor — uygulama bu konuyla ilgili bir karar
verdi. Ama `Dogrulayici.gecerliMi` bu kümenin varlığından habersiz; ona hiç parametre olarak
verilmiyor, hiç sorgulanmıyor. **GT10.** Durumsuz doğrulama yalnız belirtecin kendi
alanlarına ve verilen adıma bakar; iptal listesi gibi paylaşılan bir duruma erişimi yoktur.
**GT11.** İptal edilmiş bir belirteç, doğrulayıcı iptal listesine hiç bakmadığı için süresi
dolana kadar geçerli sayılmaya devam eder — bu derste bu süre on adım ve on birinci adımda
`gecerlilikSonu` aşıldığı için doğrulama kendiliğinden `false` dönüyor. On adımlık bu pencere
belirtecin kendi `gecerlilikSonu` değerinden geliyor; kısa tutulan bir geçerlilik süresi bu
pencereyi daraltır, ama sıfıra indirmez. Bunu iddia olarak bırakmadan koşturarak doğrulamak
için aynı ölçüm daha kısa bir geçerlilik süresiyle tekrarlanıyor.

```java
// KisaPencere.java — gecerlilik suresi kisaltilinca, iptalden sonra gecerli sayilan adim sayisi da ayni oranda kisaliyor
import java.util.*;

public class KisaPencere {
    public static void main(String[] args) {
        Belirtec kisaOmurlu = new Belirtec("kullanici-11", Map.of("rol", "calisan"), 3);

        int gecerliKalanAdim = 0;
        for (int an = 1; an <= 15; an++) {
            if (Dogrulayici.gecerliMi(kisaOmurlu, an)) gecerliKalanAdim++;
        }
        Dogrulayici.goster("gecerlilikSonu=3 belirtec, iptalden sonra gecerli sayilan adim", gecerliKalanAdim);
    }
}
```

```
gecerlilikSonu=3 belirtec, iptalden sonra gecerli sayilan adim: 3
```

`gecerlilikSonu` on yerine üç verildiğinde, iptalden sonra geçerli sayılan adım sayısı da on
yerine üç oluyor — pencere daralıyor, orantılı biçimde. Ama sıfıra hiç inmiyor, çünkü
`gecerliMi` çağrısının kendisi hâlâ yalnız bir sayı karşılaştırması; iptali anında görebilmesi
için doğrulayıcının paylaşılan bir duruma bakması gerekirdi ve o bakışı eklemek onu artık
durumsuz olmaktan çıkarırdı. Bu, dersin sınırlayıcı ölçümü: durumsuzluk bedelsiz değil, bedeli
iptalin gecikmesiyle ölçülüyor ve bu gecikme — geçerlilik süresi kısaltıldıkça küçülse de —
hiçbir zaman sıfır olmuyor.

Dersin üç ölçümü yan yana konduğunda aynı örüntü tekrar ediyor: kaynakta duran ya da hiç
durmayan bir bilgi, ancak koşturularak sayıya dönüşüyor.

| Ölçüm | Bu derste gözlenen |
|---|---|
| iddia sayısı (misafir / yönetici) | 1 / 3 |
| doğrulama kaynağı (açıklama / çağrı) | ikisi de aynı sonucu üretiyor |
| iptalden sonra geçerli kalan adım (geçerlilik süresi 10) | 10 |
| iptalden sonra geçerli kalan adım (geçerlilik süresi 3) | 3 |

Son iki satır aynı düzeneği iki ayrı parametreyle koşturuyor ve bedelin sabit değil,
geçerlilik süresiyle orantılı olduğunu gösteriyor — bu da kaynakta yazılı bir sayı değil,
iki koşumun karşılaştırılmasıyla ortaya çıkan bir ilişki.

#### Özet

- Belirteç, sunucu tarafında hiçbir oturum kaydı tutulmadan kimliğe dair bilgiyi kendi
  iddiaları içinde taşır; taşınan iddia sayısı belirteçten belirtece değişir.
- Bir yetki sorusu bir açıklamadan (`@GerekliIddia`) okunarak yanıtlanabilir; karar kaynakta
  hiçbir çağrı ifadesi doğurmaz.
- Aynı soru kaynakta yazılı bir karşılaştırmadan (çağrıdan) da yanıtlanabilir; iki kaynak
  aynı sonucu üretir, farklı yerden.
- Durumsuz doğrulama yalnız belirtecin kendi alanlarına ve verilen ana bakar; paylaşılan
  hiçbir duruma (iptal listesi gibi) erişimi yoktur.
- İptal edilmiş bir belirteç, doğrulayıcı iptal listesine hiç bakmadığı için süresi dolana
  kadar geçerli sayılmaya devam eder; bu, kaç adım sürdüğü sayılabilen bir bedeldir.

#### Sonraki Adım

Bu iki ders yetkilendirme kararının kaynağını tek bir çağrı ya da tek bir belirteç üzerinden
ölçtü. Bir kabın gerçekte kaç bileşeni ayağa kaldırdığı, hangi testin hangi parçayı kurduğu
henüz hiç sorulmadı. Sıradaki ders aynı kabı bir test ortamına koyuyor ve iki kurulumu yan
yana sayıyor: dilim testi kaç bileşeni ayağa kaldırıyor, tam bağlam testi kaç bileşeni —
ve dilimin dışında kalan bir açıklamaya ne oluyor.

### Katman Testleri

Source: https://academia.sh/tr/kurslar/kurumsal-uygulama-cercevesi/katman-testleri

Dilim testi ile tam bağlam testi arasındaki fark bir sıfat değil bir sayı; aynı kaptan geçen iki kurulum, ne kadar bileşen kurdukları sayılarak yan yana konur. Sınırlayıcı ölçüm, dilimin dışında kalan bir bileşenin kurulum açıklaması hiç çalışmasa bile dilim testinin sonucunun değişmediğini ve testin yine geçtiğini gösterir.

Önceki ders belirtecin taşıdığı iddiaları ve doğrulamanın kaynağını tek bir çağrı üzerinden
ölçtü. Bu ders geri çekilip soruyu büyütüyor: bir kabın gerçekte kaç bileşeni ayağa
kaldırdığı ve bir testin bu bileşenlerin hangisini gerçekten kurduğu. İki test kurulumu
modelleniyor — dilim testi ve tam bağlam testi — ve aralarındaki fark bir sıfatla değil bir
sayıyla ölçülüyor: kabın kurduğu bileşen sayısı. Dilim testi kaç bileşen ister, tam bağlam
testi kaç bileşen kurar ve dilimin dışında kalan bir şey bozulduğunda dilim testi bunu fark
eder mi? Katman testlerinin kapsamı ve amacı M21/K03 Tümleşim, Sözleşme ve Uçtan Uca Test ile
M08/K05 Derleme, Test ve Ekosistem kurslarında kuruldu ve burada tekrarlanmıyor; bu ders
yalnız iki kurulumun kabın gözünden ne kadar farklı olduğunu sayıyla gösteriyor.

"Katman" sözcüğü burada web-and-data-layer konusundaki katmanlara işaret ediyor —
denetleyici, servis, veri erişimi. Bir dilim testi genellikle bu katmanlardan yalnız birini
hedefliyor ve altındaki ya da üstündeki katmanı bir sahte gerçekleştirimle değiştiriyor; bu
ders o değişimin nesne kabı üzerinden nasıl yapıldığını ve sonucun nasıl sayılabildiğini
gösteriyor.

#### İki Kurulumu Yan Yana Koymak: Bileşen Sayısı

Modellenen uygulama dört parçadan kuruluyor: bir veri deposu arayüzü ve onun gerçek
gerçekleştirimi, bir servis arayüzü ve onun gerçek gerçekleştirimi, bir denetleyici bu
servise bağımlı ve servisle hiç ilgisi olmayan bağımsız bir günlük kaydedici. Aynı `Kap`
sınıfı framework-core konusunda kurulan nesne kabıyla aynı düzenek — yapıcı bağımlılıklarını
yansımayla çözüyor, `@Kur` işaretli yöntemleri kurulumdan sonra çağırıyor — burada yalnız
bu derste kullanılan parçası yeniden yazılıyor.

```java
// Kur.java — kurulumdan sonra cagrilacak yontemi isaretleyen aciklama tipi (yardimci kaynak, main yok)
import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD)
@interface Kur { }
```

```java
// Kap.java — yapici bagimliliklarini yansimayla cozen, kurdugu her bilesenin sayisini tutan kucuk bir nesne kabi (yardimci kaynak, main yok)
import java.lang.reflect.*;
import java.util.*;

class Kap {
    final Map<Class<?>, Class<?>> baglar = new HashMap<>();
    final List<Object> kurulanlar = new ArrayList<>();

    Kap bagla(Class<?> arayuz, Class<?> gerceklestirim) { baglar.put(arayuz, gerceklestirim); return this; }

    @SuppressWarnings("unchecked")
    <T> T kur(Class<T> tip) throws Exception {
        Class<?> gercek = tip.isInterface() ? baglar.get(tip) : tip;
        Constructor<?> yapici = gercek.getDeclaredConstructors()[0];
        Class<?>[] parametreTipleri = yapici.getParameterTypes();
        Object[] argumanlar = new Object[parametreTipleri.length];
        for (int i = 0; i < argumanlar.length; i++) argumanlar[i] = kur(parametreTipleri[i]);
        yapici.setAccessible(true);
        Object ornek = yapici.newInstance(argumanlar);
        for (Method m : gercek.getDeclaredMethods()) {
            if (m.isAnnotationPresent(Kur.class)) { m.setAccessible(true); m.invoke(ornek); }
        }
        kurulanlar.add(ornek);
        return (T) ornek;
    }

    static void goster(String etiket, Object deger) {
        System.out.printf("%-52s: %s%n", etiket, deger);
    }
}
```

Uygulamanın dört bileşeni ayrı dosyalarda duruyor. `GercekServis` bir kurulum kancası taşıyor —
`durum` alanı `kurulmadi` ile başlıyor, `@Kur` işaretli `hazirla` yöntemi çağrıldığında `hazir`'e
dönüyor. `SahteServis` ise dilim testinin elinde tuttuğu, gerçek servisin yerine geçen bir sahte —
hiçbir kurulum kancası yok, çünkü ihtiyaç da yok. Yapıcısı da boş: `GercekServis` bir `VeriDeposu`
istiyor; `SahteServis` hiçbir şey istemiyor. Bu fark da dilim testinin bileşen sayısını küçültüyor
— `SahteServis`'in kurulması `BellekDeposu`'nu hiç tetiklemiyor, çünkü çözülecek bir bağımlılığı
yok.

```java
// VeriDeposu.java — veri erisiminin arayuzu (yardimci kaynak, main yok)
interface VeriDeposu { String oku(); }
```

```java
// BellekDeposu.java — veri deposunun gercek gerceklestirimi (yardimci kaynak, main yok)
class BellekDeposu implements VeriDeposu {
    public String oku() { return "veri"; }
}
```

```java
// Servis.java — is mantiginin arayuzu (yardimci kaynak, main yok)
interface Servis { String islemYap(); }
```

```java
// GercekServis.java — servisin gercek gerceklestirimi; kurulum kancasi calisana kadar durumu 'kurulmadi' (yardimci kaynak, main yok)
class GercekServis implements Servis {
    final VeriDeposu depo;
    String durum = "kurulmadi";

    GercekServis(VeriDeposu depo) { this.depo = depo; }

    @Kur void hazirla() { durum = "hazir"; }

    public String islemYap() { return durum + ":" + depo.oku(); }
}
```

```java
// SahteServis.java — dilim testinin elinde tuttugu sahte gerceklestirim, hicbir kurulum kancasi tasimiyor (yardimci kaynak, main yok)
class SahteServis implements Servis {
    public String islemYap() { return "sahte-veri"; }
}
```

```java
// Denetleyici.java — servise bagimli, dilim testinin asil hedefi (yardimci kaynak, main yok)
class Denetleyici {
    final Servis servis;
    Denetleyici(Servis servis) { this.servis = servis; }
    String yanit() { return servis.islemYap(); }
}
```

```java
// GunlukKaydedici.java — servisle hicbir bagimliligi olmayan, tam baglamin bagimsiz bir parcasi (yardimci kaynak, main yok)
class GunlukKaydedici {
    String durum() { return "hazir"; }
}
```

İki ayrı nesne kabı kuruluyor: `dilimKabi`, `Servis`'i `SahteServis`'e bağlıyor; `tamKabi`, aynı arayüzü
`GercekServis`'e bağlıyor. İkisi de aynı `Denetleyici`'yi istiyor, ikisi de aynı `kur`
yöntemini çağırıyor — fark yalnız bağlama tablosunda.

```java
// BilesenSayisi.java — ayni Denetleyici, iki ayri kaptan istendiginde kac bilesen kuruluyor
public class BilesenSayisi {
    public static void main(String[] args) throws Exception {
        Kap dilimKabi = new Kap()
                .bagla(Servis.class, SahteServis.class)
                .bagla(VeriDeposu.class, BellekDeposu.class);
        Denetleyici dilimDenetleyici = dilimKabi.kur(Denetleyici.class);

        Kap tamKabi = new Kap()
                .bagla(Servis.class, GercekServis.class)
                .bagla(VeriDeposu.class, BellekDeposu.class);
        Denetleyici tamDenetleyici = tamKabi.kur(Denetleyici.class);
        tamKabi.kur(GunlukKaydedici.class);

        Kap.goster("dilim testi, kurulan bilesen sayisi", dilimKabi.kurulanlar.size());
        Kap.goster("tam baglam testi, kurulan bilesen sayisi", tamKabi.kurulanlar.size());
        Kap.goster("dilim testi, Denetleyici.yanit()", dilimDenetleyici.yanit());
        Kap.goster("tam baglam testi, Denetleyici.yanit()", tamDenetleyici.yanit());
    }
}
```

```
dilim testi, kurulan bilesen sayisi                 : 2
tam baglam testi, kurulan bilesen sayisi            : 4
dilim testi, Denetleyici.yanit()                    : sahte-veri
tam baglam testi, Denetleyici.yanit()               : hazir:veri
```

**GT12.** Dilim testi de tam bağlam testi de aynı `Kap` sınıfından geçiyor; aralarındaki fark
kodun yapısında değil, kabın kurduğu bileşen sayısında ölçülüyor. **GT13.** Dilim testi
sınadığı `Denetleyici`'nin doğrudan bağımlılığını bile nesne kabı üzerinden bir sahte
gerçekleştirimle kuruyor — `SahteServis` de `kurulanlar` listesine giriyor — ama toplam
bileşen sayısı yine tam bağlam testinden az: iki değil dört. **GT14.** Tam bağlam testi
bağımlılık zincirinin tamamını (`GercekServis`, `BellekDeposu`) ve zincire hiç girmeyen
bağımsız bileşenleri (`GunlukKaydedici`) de kuruyor; `Denetleyici`'nin ihtiyaç duymadığı bir
bileşen bile tam bağlamın parçası oluyor, çünkü tam bağlam testi bütün uygulamayı ayağa
kaldırıyor.

`kur` yönteminin kendisi hangi testin hangi kurulumda olduğunu hiç bilmiyor; tek bildiği
bağlama tablosu ve yapıcı imzaları. Aynı yöntem, `Servis` parametresini gördüğünde
`baglar` haritasına bakıyor ve orada ne bulursa onu kuruyor — `dilimKabi` için
`SahteServis`, `tamKabi` için `GercekServis`. Fark testin niyetinde, kodun kendisinde değil.
Bu da bileşen sayısının neden önemli olduğunu gösteriyor: dilim testi az bileşen kurduğu
için hızlı çalışıyor, çünkü yansımayla kurulan her bileşen bir yapıcı çözümlemesi ve
gerekiyorsa bir kurulum kancası çağrısı taşıyor; tam bağlam testi bunların hepsini yapıyor
ve dört katına çıkan bileşen sayısı kabaca dört katına çıkan bir kurulum maliyeti demek.

#### Dilimin Dışında Kalan Açıklama

`GercekServis` sınıfının kurulum kancası yalnız nesne kabı onu kurduğunda çalışıyor. Aynı sınıf elle
`new` ile kurulursa `@Kur` işaretli `hazirla` yöntemi hiç çağrılmıyor — bu, framework-core
konusunda kurulan sınırlayıcı okumanın aynısı, burada bir servis sınıfı üzerinde tekrar
ölçülüyor.

```java
// DilimDisindaKalan.java — GercekServis'in kurulum kancasi nesne kabi disinda hic calismiyor; dilim testi bundan tamamen bagimsiz
public class DilimDisindaKalan {
    public static void main(String[] args) throws Exception {
        Kap dilimKabi = new Kap()
                .bagla(Servis.class, SahteServis.class)
                .bagla(VeriDeposu.class, BellekDeposu.class);
        Denetleyici dilimDenetleyici = dilimKabi.kur(Denetleyici.class);

        Kap tamKabi = new Kap()
                .bagla(Servis.class, GercekServis.class)
                .bagla(VeriDeposu.class, BellekDeposu.class);

        GercekServis elleKurulan = new GercekServis(new BellekDeposu());
        Servis kapUzerindenKurulan = tamKabi.kur(Servis.class);

        Kap.goster("GercekServis, elle kurulmus, @Kur hic calismadi", elleKurulan.islemYap());
        Kap.goster("GercekServis, nesne kabi uzerinden kurulmus, @Kur calisti", kapUzerindenKurulan.islemYap());
        Kap.goster("dilim testi, Denetleyici.yanit() (GercekServis'ten bagimsiz)", dilimDenetleyici.yanit());
    }
}
```

```
GercekServis, elle kurulmus, @Kur hic calismadi     : kurulmadi:veri
GercekServis, nesne kabi uzerinden kurulmus, @Kur calisti: hazir:veri
dilim testi, Denetleyici.yanit() (GercekServis'ten bagimsiz): sahte-veri
```

İlk iki satır aynı sınıfın iki farklı kuruluş biçimini gösteriyor: elle kurulduğunda
`kurulmadi:veri`, nesne kabı üzerinden kurulduğunda `hazir:veri` — açıklama yalnız ikincisinde
uygulanıyor. **GT15.** Bir kurulum açıklaması yalnız nesne kabı onu kurduğunda uygulanır; elle
kurulan aynı sınıf açıklamayı hiç görmez, çünkü açıklamayı okuyan ve çağıran taraf kabın
kendisidir. Üçüncü satır ise dersin asıl noktası: dilim testinin sonucu — `sahte-veri` —
`GercekServis`'in elle mi nesne kabı üzerinden mi kurulduğundan tamamen bağımsız, çünkü dilim testi
`GercekServis`'i hiç görmüyor. `GercekServis`'in kurulum kancası bozulsa, kaldırılsa, hatta
sınıfın kendisi tamamen silinse, dilim testi bundan haberdar olmaz.

**GT16.** Dilimin dışında kalan bir bileşenin açıklaması hiç uygulanmasa bile dilim testinin
sonucu değişmez ve test yine geçer — bu, dersin sınırlayıcı ölçümü. Dilim testi
`Denetleyici`'nin `Servis`'i doğru çağırıp çağırmadığını doğruluyor; `GercekServis`'in
kurulum kancasının çalışıp çalışmadığını hiç doğrulamıyor, çünkü doğrulama alanının dışında.
Daha az bileşen kurmak daha az şey doğrulamak demek — dilim testi hızlı ve odaklı, ama
`GercekServis`'e ait her açıklama, her kurulum kancası, dilimin görüş alanının tamamen
dışında kalıyor. Ortak tanımın kusur sınıflaması bu durumu tam karşılıyor: dilim testi
açısından `GercekServis`'in bozulan kurulum kancası ne `hata` veriyor ne `sessiz` kalıyor —
testin kendisi açısından tümüyle **etkisiz**, çünkü test o kancanın varlığından habersiz. Bu boşluğu kapatan tek şey tam bağlam testi, çünkü yalnız o kurulum
`GercekServis`'i gerçekten ayağa kaldırıyor ve kancasının çalıştığını kontrol edebiliyor.

İki test türü birbirinin yerine geçmiyor, ikisi farklı sorulara yanıt veriyor:

| Soru | Dilim testi | Tam bağlam testi |
|---|---|---|
| Kurulan bileşen sayısı | 2 | 4 |
| `Denetleyici`'nin `Servis`'i doğru çağırdığı doğrulanıyor mu | evet | evet |
| `GercekServis`'in kurulum kancası doğrulanıyor mu | hayır | evet |
| `GunlukKaydedici`'nin varlığı doğrulanıyor mu | hayır | evet |

Tablo tek bir sonuca çıkıyor: dilim testinin ucuzluğu bir eksiklikle geliyor ve bu
eksiklik rastgele değil — kabın o testte hiç kurmadığı her bileşen, dilimin görüş alanının
dışında kalıyor. Bir kod tabanı yalnız dilim testleriyle korunuyorsa, bileşenler arasındaki
gerçek bağlantı hiçbir zaman koşturarak sınanmamış oluyor; bu boşluğu ancak tam bağlam
testi, kurduğu bileşen sayısıyla orantılı bir maliyet karşılığında kapatabiliyor.

Pratikte bir kod tabanı ikisini birden tutuyor, ikisini de aynı görevle değil: dilim
testleri her değişiklikte sık koşuyor, çünkü az bileşen kurdukları için hızlılar ve
`Denetleyici` gibi tek bir katmanın davranışını hızla doğruluyor; tam bağlam testleri daha
seyrek koşuyor, çünkü daha çok bileşen kurdukları için daha yavaşlar, ama karşılığında
bileşenler arasındaki gerçek bağlantıyı sınıyor. Bu derste ölçülen sayı — dilim testinde 2,
tam bağlamda 4 — küçük bir örnek üzerinde; gerçek bir uygulamada bu oran onlarca ile yüzlerce
arasında değişebilir ve fark büyüdükçe dilim testinin doğrulamadığı yüzey de büyüyor.

#### Özet

- Dilim testi ile tam bağlam testi aynı kaptan geçer; aralarındaki fark kabın kurduğu
  bileşen sayısıyla ölçülür.
- Dilim testi, sınadığı sınıfın doğrudan bağımlılığını bile bir sahte gerçekleştirimle
  kurabilir; buna rağmen toplam bileşen sayısı tam bağlam testinden azdır.
- Tam bağlam testi bağımlılık zincirinin tamamını ve zincire hiç girmeyen bağımsız
  bileşenleri de kurar.
- Bir kurulum açıklaması yalnız nesne kabı onu kurduğunda uygulanır; elle kurulan aynı sınıf
  açıklamayı hiç görmez.
- Dilimin dışında kalan bir bileşenin açıklaması hiç çalışmasa bile dilim testinin sonucu
  değişmez ve test yine geçer; daha az bileşen kurmak daha az şey doğrulamak demektir.

#### Sonraki Adım

Bu ders hangi bileşenlerin kurulduğunu saydı; sıradaki ders hiç kurulmayan, hiç yazılmayan
bir şeyi soruyor. Bir uygulamanın çalışır durumda olup olmadığını dışarıdan soran uçlar var
ve bu uçların çoğu kaynakta hiç görünmüyor — ne bir sınıf, ne bir yöntem, ne bir açıklama.
Sıradaki ders bu uçların kaç tanesinin hiç yazılmadan var olduğunu ve hangi bilginin
dışarıya verildiğini ölçüyor.

### Sağlık ve Ölçüm Uçları

Source: https://academia.sh/tr/kurslar/kurumsal-uygulama-cercevesi/saglik-ve-olcum-uclari

Bir uygulama hiç yazılmadan var olan uçlarla ayağa kalkar — kabın kendisi ekler; yeni bir ölçüm ucu bir açıklamayla eklenebilir. Sınırlayıcı ölçüm ayrı bir daraltma bölümünde kapanır — kabın kurduğu varsayılan uçlar her bilgiyi açmaz, hangi alanın dışarı verildiği açık bir işaretlemeye bağlıdır ve bu işaretleme yanlış bir alana konursa nesne kabı hiçbir hata vermeden onu da dışarı verir.

Önceki ders hangi bileşenlerin kurulduğunu saydı — dilim testi iki, tam bağlam testi dört
bileşen kurmuştu ve ikisi de kaynakta yazılı bir kararın sonucuydu: bağlama tablosu.
Bu ders hiç kurulmayan, hiç yazılmayan bir şeyi soruyor: bir uygulamanın çalışır durumda
olup olmadığını dışarıdan soran uçlar var ve bu uçların çoğu kaynakta hiç görünmüyor — ne
bir sınıf, ne bir yöntem, ne bir açıklama. Sağlık
ve ölçüm uçlarının kavramı ve gözlemlenebilirliğin genel çerçevesi M22/K06 Gözlemlenebilirlik
ve İşletim kursunda kuruldu ve burada tekrarlanmıyor; bu ders yalnız bu uçların kaynakta ne
kadar iz bıraktığını ve hangi bilginin dışarı verildiğini sayıyla ölçüyor. Gerçek bir ölçüm
değeri, gerçek bir süre ya da gerçek bir bellek kullanımı yazılmıyor; sayılan şey uç sayısı ve
alan varlığı. Ölçümün üçüncü bölümü ayrı bir daraltma bölümünde duruyor, çünkü varsayılan
uçların var olması her bilginin dışarı verildiği anlamına gelmiyor — bu, ayrı bir karar.

Gerçek bir uygulamada sağlık ucu genellikle tek bir sözcük döndürür — çalışıyor mu,
çalışmıyor mu — ve ölçüm ucu sayaçlar, oranlar, iç durumlar taşır. Bu ders ikisini de
modelliyor, ama sorduğu soru sağlık ile ölçüm arasındaki fark değil; ikisinin de ortak
noktası: kaynakta bir çağrı olmadan var olabilmeleri. `/saglik` bir sağlık ucu, `/olcum/…`
öneki taşıyan her uç bir ölçüm ucu, ama ikisi de aynı kurala tabi — kabın kendisi ya
otomatik olarak ekliyor ya da bir işaretten okuyor.

#### Uçlar: Kaynakta Hiç Geçmeyenler ve Açıklamadan Gelenler

Modellenen nesne kabı kurulduğu anda iki uçla doğuyor: `/saglik` ve `/canlilik`. Bu iki uç
hiçbir uygulama sınıfının, hiçbir açıklamanın sonucu değil — kabın kurucusu içine yazılmış,
her kurulumda otomatik olarak var olan bir çift. Üçüncü bir uç türü de var: bir bileşenin
alanına konan `@OlcumUcu` işareti, nesne kabı tarafından okunup yeni bir uç olarak kaydediliyor.

```java
// OlcumUcu.java — bir alani disari verilecek bir olcum ucu olarak isaretleyen aciklama tipi (yardimci kaynak, main yok)
import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD)
@interface OlcumUcu { String ad(); }
```

```java
// Kap.java — kurulur kurulmaz iki varsayilan ucu var eden, aciklamayla yeni uc ekleyebilen kucuk bir nesne kabi (yardimci kaynak, main yok)
import java.lang.reflect.*;
import java.util.*;
import java.util.function.*;

class Kap {
    final Map<String, Supplier<String>> uclar = new LinkedHashMap<>();
    final int varsayilanUcSayisi;

    Kap() {
        // varsayilan uclar: hicbir uygulama sinifi bunlari hic cagirmiyor, kabin kendisi ekliyor
        uclar.put("/saglik", () -> "calisiyor");
        uclar.put("/canlilik", () -> "evet");
        varsayilanUcSayisi = uclar.size();
    }

    void kaydet(Object bilesen) {
        for (Field f : bilesen.getClass().getDeclaredFields()) {
            OlcumUcu o = f.getAnnotation(OlcumUcu.class);
            if (o == null) continue;
            f.setAccessible(true);
            uclar.put("/olcum/" + o.ad(), () -> {
                try { return String.valueOf(f.get(bilesen)); }
                catch (Exception e) { return "?"; }
            });
        }
    }

    String cagir(String yol) {
        Supplier<String> s = uclar.get(yol);
        return s == null ? "yok" : s.get();
    }

    static void goster(String etiket, Object deger) {
        System.out.printf("%-46s: %s%n", etiket, deger);
    }
}
```

```java
// Uygulama.java — istek sayisi disari verilecek sekilde isaretlenmis, kalan iki alan hic isaretlenmemis (yardimci kaynak, main yok)
class Uygulama {
    @OlcumUcu(ad = "istek-sayisi") int istekSayaci = 42;
    String dahiliAnahtar = "ic-yapilandirma";
    String surum = "3";
}
```

```java
// VarsayilanUclar.java — nesne kabi kurulur kurulmaz var olan uc sayisi, ve bir bilesen kaydedildikten sonraki toplam
public class VarsayilanUclar {
    public static void main(String[] args) {
        Kap kap = new Kap();
        Kap.goster("nesne kabi kurulur kurulmaz var olan uc sayisi", kap.varsayilanUcSayisi);
        Kap.goster("bu uclari kaynakta cagiran uygulama sinifi sayisi", 0);

        kap.kaydet(new Uygulama());
        Kap.goster("Uygulama kaydedildikten sonra toplam uc sayisi", kap.uclar.size());
        Kap.goster("/saglik", kap.cagir("/saglik"));
        Kap.goster("/canlilik", kap.cagir("/canlilik"));
        Kap.goster("/olcum/istek-sayisi", kap.cagir("/olcum/istek-sayisi"));
    }
}
```

```
nesne kabi kurulur kurulmaz var olan uc sayisi: 2
bu uclari kaynakta cagiran uygulama sinifi sayisi: 0
Uygulama kaydedildikten sonra toplam uc sayisi: 3
/saglik                                       : calisiyor
/canlilik                                     : evet
/olcum/istek-sayisi                           : 42
```

**GT17.** Kabın kurulduğu anda, hiçbir uygulama sınıfı hiç yazılmadan var olan uçlar vardır;
bunlar varsayılandan doğar — `/saglik` ile `/canlilik`'in kaynakta karşılığı `Kap`'ın
kurucusundaki iki satır ve bu satırları hiçbir uygulama kodu çağırmıyor. **GT18.** Yeni bir
ölçüm ucu bir açıklamayla eklenir; `istekSayaci` alanının üzerindeki `@OlcumUcu` işareti,
`/olcum/istek-sayisi` ucunu doğuruyor, ama bu ucu okuyan çağrı `Uygulama` sınıfının içinde
hiç yok — okuyan ve kaydeden taraf `Kap`'ın kendisi. Toplam üç uçtan ikisi kaynakta hiç
geçmeden, biri bir işaretle var oluyor; kaynağı yalnızca okuyan biri `Uygulama.java`'da tek
satır görür, ama uygulamanın dışarıya verdiği yüzey üç uçtan oluşuyor.

Varsayılan uçların sayısı sabit ve `Kap`'ın kurucusunda okunabilir; kaç tane olduğunu öğrenmek
için hiçbir uygulama sınıfını taramaya gerek yok; `varsayilanUcSayisi` alanı kurulur kurulmaz bu
sayıyı taşıyor. Açıklamadan gelen uçlar için durum farklı: onların sayısı `kaydet` çağrısının kaç
kez ve hangi sınıflarla yapıldığına bağlı ve bu sayı ancak uygulamanın bütün bileşenleri
kaydedildikten sonra kesinleşiyor. İki türün bu farkı — biri sabit, biri kurulum zamanında büyüyen
— dersin ilerleyen bölümünde tekrar karşımıza çıkacak: dağıtık yapılandırma dersinde de bazı
değerler kurulurken sabitlenirken bazıları kurulumdan sonra değişebilecek.

#### Neyin Dışarı Verildiği Ayrı Bir Karardır

`Uygulama` sınıfı üç alan taşıyor, ama yalnız biri `/olcum` altında görünüyor. Kalan iki
alan — `dahiliAnahtar`, `surum` — sınıfın içinde duruyor, hiçbir uç onları anmıyor, çünkü
üzerlerinde hiçbir işaret yok. Bu, dersin en önemli ayrımı: bir bileşenin taşıdığı bilgi
miktarı ile o bileşenin dışarı verdiği bilgi miktarı aynı şey değil.

```java
// AlanSayisi.java — Uygulama'nin tasidigi toplam alan sayisi ile isaretli alan sayisi karsilastiriliyor
import java.lang.reflect.*;

public class AlanSayisi {
    public static void main(String[] args) {
        Kap kap = new Kap();
        Uygulama uygulama = new Uygulama();
        kap.kaydet(uygulama);

        int toplamAlan = Uygulama.class.getDeclaredFields().length;
        long isaretliAlan = 0;
        for (Field f : Uygulama.class.getDeclaredFields()) {
            if (f.isAnnotationPresent(OlcumUcu.class)) isaretliAlan++;
        }

        Kap.goster("Uygulama sinifinin tasidigi toplam alan sayisi", toplamAlan);
        Kap.goster("@OlcumUcu ile isaretli alan sayisi", isaretliAlan);
        Kap.goster("/olcum/dahili-anahtar (hic isaretlenmedi)", kap.cagir("/olcum/dahili-anahtar"));
        Kap.goster("/olcum/surum (hic isaretlenmedi)", kap.cagir("/olcum/surum"));
    }
}
```

```
Uygulama sinifinin tasidigi toplam alan sayisi: 3
@OlcumUcu ile isaretli alan sayisi            : 1
/olcum/dahili-anahtar (hic isaretlenmedi)     : yok
/olcum/surum (hic isaretlenmedi)              : yok
```

Üç alanın yalnız biri açığa çıkıyor; kalan ikisi için `cagir` yöntemi `yok` döndürüyor,
çünkü `Kap`'ın `uclar` haritasında böyle bir anahtar hiç kayıtlı değil. **GT19.** Bir
bileşenin taşıdığı alan sayısı ile dışarı verilen alan sayısı aynı değildir; yalnız
işaretli alanlar dışarı çıkar, işaretsiz her alan kabın hiç bilmediği bir alan olarak
kalır. **GT20.** Bu, dersin sınırlayıcı ölçümünün ilk yarısı: varsayılan uçların var olması
her bilginin açıldığı anlamına gelmiyor. `/saglik` ve `/canlilik` var, ama bu ikisi
`Uygulama`'nın iç durumuna hiç dokunmuyor; iç durumdan ne çıkacağı yalnızca işaretlemeyle
belirleniyor ve işaretleme yoksa hiçbir şey çıkmıyor. `Kap`'ın `kaydet` yöntemi
`Uygulama.class.getDeclaredFields()` ile bütün alanları görüyor — `dahiliAnahtar` ve
`surum` da bu taramadan geçiyor — ama işaret taşımayan her alan döngüde `continue` ile
atlanıyor. Görülmek ile dışarı verilmek bu yüzden aynı şey değil: nesne kabı her alanı görüyor,
yalnız işaretlenmiş olanı kaydediyor.

#### Genişletmenin Bedeli: Yanlış İşaretlenen Bir Alan

İşaretleme mekanizması iki yönlü çalışıyor: bir alanı `@OlcumUcu` ile işaretlemek onu
görünür kılıyor, işaretlemeyi doğru alana koymak ise ayrı bir sorumluluk. `Kap`'ın `kaydet`
yöntemi işaretli her alanı aynı biçimde işliyor — hangi alanın "güvenli" hangisinin
"hassas" olduğuna dair hiçbir ayrım taşımıyor.

```java
// HassasBilesen.java — iki alan da isaretli; biri olculmek istenen sayac, biri yanlislikla isaretlenmis ic bilgi (yardimci kaynak, main yok)
class HassasBilesen {
    @OlcumUcu(ad = "yapilandirma-anahtari") String dahiliAnahtar = "ic-yapilandirma-degeri";
    @OlcumUcu(ad = "aktif-oturum") int aktifOturumSayisi = 7;
}
```

```java
// YanlisIsaretleme.java — nesne kabi iki isareti de ayni bicimde isliyor, hangisinin yanlislikla konuldugunu ayirt etmiyor
public class YanlisIsaretleme {
    public static void main(String[] args) {
        Kap kap = new Kap();
        kap.kaydet(new HassasBilesen());

        Kap.goster("/olcum/aktif-oturum (kastedilen olcum)", kap.cagir("/olcum/aktif-oturum"));
        Kap.goster("/olcum/yapilandirma-anahtari (yanlislikla isaretlendi)", kap.cagir("/olcum/yapilandirma-anahtari"));
        Kap.goster("nesne kabi bu isaretlemeyi reddetti mi", "hayir, hicbir hata verilmedi");
    }
}
```

```
/olcum/aktif-oturum (kastedilen olcum)        : 7
/olcum/yapilandirma-anahtari (yanlislikla isaretlendi): ic-yapilandirma-degeri
nesne kabi bu isaretlemeyi reddetti mi        : hayir, hicbir hata verilmedi
```

`aktifOturumSayisi` tam da amaçlanan şeyi ölçüyor — kaç oturum açık. `dahiliAnahtar` ise
aynı işaretle yanlışlıkla dışarı verildi ve `Kap` bu iki durumu ayırt etmiyor: ikisi de
`@OlcumUcu` taşıyor, ikisi de aynı yoldan geçip aynı biçimde kaydediliyor. `kaydet`
yönteminin kendisi bu iki alan arasında hiçbir fark görmüyor, çünkü zaten hiçbir fark
gözlemleyecek bilgiye sahip değil — elinde yalnız bir alan adı, bir açıklama ve bir
değer var; alanın "hassas" mı "zararsız" mı olduğuna dair hiçbir sınıflandırma yansımanın
kendisinde bulunmuyor. Bu sınıflandırmayı yapacak taraf yalnızca `@OlcumUcu` işaretini
koyan kişi ve o karar bir kez yanlış verildiğinde kabın kendisi bunu düzeltmiyor. **GT21.**
Yanlış bir alana konan ölçüm işareti nesne kabı tarafından hiçbir hata verilmeden dışarı
verilir; bu, dersin sınırlayıcı ölçümünün ikinci yarısı. İlk yarı neyin dışarı **verilmediğini**
gösterdi — işaretsiz alan hiç çıkmaz; ikinci yarı neyin yanlışlıkla dışarı
**verilebileceğini** gösteriyor — işaretli her şey, doğru işaretlenmiş olsun ya da olmasın,
çıkar. Sağlık ve ölçüm uçlarının kurduğu görünürlük bu yüzden tek yönlü bir güvenlik değil;
hangi bilginin dışarı verildiği kaynaktaki işaretlerin toplamı kadar, o işaretlerin doğru
konulup konulmadığına da bağlı.

Bu dersin dört ölçümü, hangi bilginin nereden geldiğini ve nereye çıktığını tek bir
tabloda topluyor:

| Soru | Yanıt | Kaynaktaki karşılığı |
|---|---|---|
| Kaç uç hiç kod yazılmadan var oldu | 2 (`/saglik`, `/canlilik`) | yok — kabın kurucusu |
| Kaç uç bir açıklamayla eklendi | 1 (`/olcum/istek-sayisi`) | `@OlcumUcu` işareti |
| Kaç alan taşınıyor, kaçı dışarı çıkıyor | 3 alan, 1 dışarı çıkan | işaretin varlığı |
| Yanlış işaretlenen alan reddediliyor mu | hayır | işaretin doğruluğu denetlenmiyor |

Tablonun son satırı ilk üçünden farklı bir sınıfa ait: ilk üçü "ne var" sorusuna sayıyla
yanıt veriyor, son satır "bu sayının ne kadar güvenilir olduğu" sorusuna yanıt veriyor —
ve yanıt olumsuz. Bir uç noktanın var olması onun doğru amaçla var olduğu anlamına gelmiyor.

#### Özet

- Kabın kurulduğu anda, hiçbir uygulama sınıfı hiç yazılmadan var olan uçlar vardır; bunlar
  varsayılandan doğar.
- Yeni bir ölçüm ucu bir açıklamayla eklenir; kaynakta bu ucu çağıran hiçbir satır yoktur.
- Bir bileşenin taşıdığı toplam alan sayısı ile dışarı verilen alan sayısı aynı değildir;
  yalnız işaretli alanlar dışarı çıkar.
- Varsayılan uçların var olması her bilginin dışarı verildiği anlamına gelmez; hangi
  bilginin açıldığı ayrı, açık bir işaretleme kararıdır.
- Yanlış bir alana konan ölçüm işareti nesne kabı tarafından hiçbir hata verilmeden dışarı verilir;
  işaretleme mekanizması "doğru" ile "yanlış" işaretlemeyi ayırt etmez.

Dört ölçümün ortak noktası, üçü de aynı kaynağı paylaşıyor olması: `Kap`'ın kendi kararı.
İki varsayılan uç kabın kurucusunda, açıklama tabanlı uçların kabulü `kaydet` yönteminde,
yanlış işaretlemenin fark edilmemesi de yine `kaydet` yönteminin ne yapmadığında yatıyor.
Uygulama kodu bu üç kararın hiçbirini vermiyor; yalnız işareti koyuyor ya da koymuyor, geri
kalanı kabın kuralı.

#### Sonraki Adım

Bu ders bir bileşenin iç durumunun dışarı nasıl sızdığını ölçtü. Sıradaki ders tersini
soruyor: dışarıdaki bir değer içeri nasıl giriyor ve bir kez girdikten sonra o değer
değiştiğinde hangi bileşen bunu görüyor. Bir uygulamanın birden çok parçası aynı
yapılandırma değerine bakıyorsa, değer değiştiğinde hepsinin aynı anda haberdar olup
olmadığı hiç açık değil — sıradaki ders bunu koşturarak ölçüyor.

### Dağıtık Yapılandırma

Source: https://academia.sh/tr/kurslar/kurumsal-uygulama-cercevesi/dagitik-yapilandirma

Merkezî bir yapılandırma değeri dışarıda değişir, ama bunu kim gördüğü bileşenin kapsamına bağlıdır — tekil bir bileşen kurulduğu andaki değeri sonsuza kadar taşır, her istekte yeniden kurulan bileşen değişikliği bir sonraki kurulumda görür. Sınırlayıcı ölçüm, kursun en sessiz kusurunu ölçer — hiçbir hata çıkmaz, aynı anda var olan iki bileşen aynı anahtara iki farklı yanıt verir ve fark ancak yan yana konduğunda görünür.

Önceki ders bir bileşenin iç durumunun dışarı nasıl sızdığını ölçtü — hangi alanın
işaretlendiği, hangisinin işaretlenmediği. Bu ders tersini soruyor: dışarıdaki bir değer
içeri nasıl giriyor ve bir kez girdikten sonra o değer değiştiğinde hangi bileşen bunu
görüyor. Merkezî yapılandırma modelleniyor — değerler
uygulamanın dışında tutuluyor, bir bileşenin kodunda değil — ve dışarıdaki bir değer
değiştiğinde hangi bileşenin bunu ne zaman gördüğü ölçülüyor. Merkezî yapılandırmanın ve
dış yapılandırma deposunun kavramı M22/K05 Kod Olarak Altyapı ile M22/K08 Platform
Mühendisliği kurslarında kuruldu ve burada tekrarlanmıyor; bu ders yalnız değerin hangi
bileşene ne zaman ulaştığını ölçüyor. Sınırlayıcı ölçüm kursun en sessiz kusurunu taşıyor:
hiçbir hata çıkmıyor, iki bileşen aynı anda iki farklı değeri doğru sanıyor.

`MerkeziAyarlar` bu derste tek bir Java sınıfı olarak modelleniyor — gerçek bir dağıtık
sistemde bu depo ayrı bir süreçte, hatta ayrı bir makinede yaşar ve bileşenler ona ağ
üzerinden ulaşır. Gerçek bir ağ çağrısı burada yok; modellenen şey ağın kendisi değil, ağın
yarattığı **zamanlama sorunu** — bir değer bir yerde değişir ve bu değişikliğin başka bir
yere ulaşması sıfır zaman almaz. Statik bir Java alanı bu gecikmeyi taklit etmiyor, ama
"dışarıda tutulan tek bir değer, birden çok bileşen tarafından farklı anlarda okunuyor"
yapısını birebir taşıyor — ölçülen de tam bu yapı.

#### Dışarıda Değişen Bir Değer

`MerkeziAyarlar`, uygulamanın hiçbir bileşenine ait olmayan, dışarıda tutulan bir
anahtar-değer deposu. Bir bileşen bu değeri ne zaman okursa, o an depoda ne varsa onu görür
— ama bu okuma yalnız bir kez mi oluyor, yoksa her seferinde mi, bu bileşenin
**kapsamına** bağlı. Kapsam kavramı framework-core konusunda kuruldu: tekil bir bileşen bir
kez kurulur ve o örnek sürekli yeniden kullanılır, her istekte kurulan bileşen ise her
istek için yeniden inşa edilir.

```java
// MerkeziAyarlar.java — uygulamanin disinda tutulan, disaridan degistirilebilen anahtar-deger deposu (yardimci kaynak, main yok)
import java.util.*;

class MerkeziAyarlar {
    static final Map<String, String> degerler = new HashMap<>();
    static { degerler.put("mod", "normal"); }

    static String getir(String anahtar) { return degerler.get(anahtar); }
    static void guncelle(String anahtar, String yeni) { degerler.put(anahtar, yeni); }
}
```

```java
// TekilBilesen.java — degeri yalnizca kuruldugu anda okuyup alanda saklayan tekil bilesen (yardimci kaynak, main yok)
class TekilBilesen {
    final String modDegeri;
    TekilBilesen() { modDegeri = MerkeziAyarlar.getir("mod"); }
    String mod() { return modDegeri; }
}
```

```java
// HerIstekteBilesen.java — her istekte yeniden kuruldugu icin degeri her seferinde yeniden okuyan bilesen (yardimci kaynak, main yok)
class HerIstekteBilesen {
    final String modDegeri;
    HerIstekteBilesen() { modDegeri = MerkeziAyarlar.getir("mod"); }
    String mod() { return modDegeri; }
}
```

```java
// Olcum.java — sonuc yazdirma yardimcisi (yardimci kaynak, main yok)
class Olcum {
    static void goster(String etiket, Object deger) {
        System.out.printf("%-52s: %s%n", etiket, deger);
    }
}
```

`TekilBilesen` ile `HerIstekteBilesen`'in yapıcıları birebir aynı satırı taşıyor:
`MerkeziAyarlar.getir("mod")`. Aralarındaki fark kodda değil, kaç kez kurulduklarında —
`TekilBilesen`'in yapıcısı uygulamanın ömrü boyunca yalnız bir kez çalışıyor,
`HerIstekteBilesen`'inki her yeni istek geldiğinde yeniden.

```java
// DisardaDegisen.java — ayni okuma satiri, tekil bilesende bir kez, her istekte kurulan bilesende her kurulumda calisiyor
public class DisardaDegisen {
    public static void main(String[] args) {
        TekilBilesen tekil = new TekilBilesen();
        Olcum.goster("tekil bilesen, degisiklikten once", tekil.mod());

        MerkeziAyarlar.guncelle("mod", "bakim");
        Olcum.goster("disaridaki deger degisti mi", MerkeziAyarlar.getir("mod"));

        Olcum.goster("ayni tekil bilesen, degisiklikten sonra", tekil.mod());

        HerIstekteBilesen yeniIstek = new HerIstekteBilesen();
        Olcum.goster("degisiklikten sonra yeniden kurulan bilesen", yeniIstek.mod());
    }
}
```

```
tekil bilesen, degisiklikten once                   : normal
disaridaki deger degisti mi                         : bakim
ayni tekil bilesen, degisiklikten sonra             : normal
degisiklikten sonra yeniden kurulan bilesen         : bakim
```

**GT22.** Merkezî yapılandırma dışarıda tutulur, hiçbir bileşenin kendi alanında değil;
değerin ne zaman okunduğu, framework-core konusunda kurulan kapsam ayrımına bağlı —
tekrarlanmıyor, yalnız burada değerin **zamanlamasını** belirlediği ölçülüyor. **GT23.**
Tekil bir bileşen değeri yalnız kurulduğu anda okur ve alanında saklar; dışarıdaki
değişiklik o örneğe hiç ulaşmaz — `tekil.mod()` üçüncü satırda hâlâ `normal` diyor, oysa
depo çoktan `bakim`'e döndü. **GT24.** Her istekte yeniden kurulan bir bileşen değeri her
seferinde yeniden okur; değişiklikten sonra kurulan `yeniIstek`, aynı satırı çalıştırıp
güncel değeri görüyor.

Bu fark her istekte kurulan bileşenin "daha doğru" olduğu anlamına gelmiyor — yalnız daha
**güncel**. Tekil bileşen bir kez kurulup sürekli yeniden kullanıldığı için ucuz; her
istekte kurulan bileşen her seferinde yapıcısını yeniden çalıştırdığı için daha pahalı, ama
karşılığında dışarıdaki her değişikliği bir sonraki kurulumda görüyor. İki kapsamın hangisi
seçilirse seçilsin, seçim kaynakta bir kez yazılıyor ve o sınıfın bütün örnekleri için aynı
kalıyor; bir bileşenin bazen tekil bazen her istekte davranması bu modelde yok.

#### Yenileme Bir Çağrıdır, Otomatik Değil

Bir tekil bileşenin dışarıdaki değişikliği hiç görmemesi her zaman istenen bir şey değil.
Bazı bileşenler kendilerine açıkça bir yenileme yöntemi ekliyor — ama bu yöntem çağrılmadığı
sürece hiçbir şey değişmiyor.

```java
// YenilenebilirBilesen.java — tekil, ama disaridan cagrilan bir yontemle degerini yeniden okuyabiliyor (yardimci kaynak, main yok)
class YenilenebilirBilesen {
    String modDegeri;
    YenilenebilirBilesen() { modDegeri = MerkeziAyarlar.getir("mod"); }
    String mod() { return modDegeri; }
    void yenile() { modDegeri = MerkeziAyarlar.getir("mod"); }
}
```

```java
// YenilemeCagridir.java — disaridaki deger degisiyor, ama yenile() cagrilana kadar bilesen bunu gormuyor
public class YenilemeCagridir {
    public static void main(String[] args) {
        MerkeziAyarlar.guncelle("mod", "normal");
        YenilenebilirBilesen bilesen = new YenilenebilirBilesen();
        Olcum.goster("yenilenebilir bilesen, kurulusta", bilesen.mod());

        MerkeziAyarlar.guncelle("mod", "bakim");
        Olcum.goster("disaridaki deger degisti, yenile() hic cagrilmadi", bilesen.mod());

        bilesen.yenile();
        Olcum.goster("yenile() cagrildiktan sonra", bilesen.mod());
    }
}
```

```
yenilenebilir bilesen, kurulusta                    : normal
disaridaki deger degisti, yenile() hic cagrilmadi   : normal
yenile() cagrildiktan sonra                         : bakim
```

**GT25.** Yenileme kendiliğinden olmaz, açık bir çağrıdır; `yenile()` çağrılmadıkça tekil
bir bileşen eski değeri tutmaya devam eder, dışarıdaki değişiklik ile bileşenin gördüğü
değer arasındaki fark yalnız o çağrı geldiğinde kapanır. Bu satır `TekilBilesen`'in hiç
sahip olmadığı bir şey ekliyor: dışarıdaki değişikliği görme **imkânı**. Ama imkân otomatik
uygulanma anlamına gelmiyor — `YenilenebilirBilesen` de tıpkı `TekilBilesen` gibi ikinci
satırda hâlâ `normal` diyor, çünkü `yenile()` henüz çağrılmadı. Yenileme bir varsayılan
değil, bir karar.

Bu üç bileşen türü — tekil, her istekte kurulan, yenilenebilir tekil — dışarıdaki bir
değişikliğe karşı üç ayrı duruş sergiliyor: birincisi hiç görmüyor, ikincisi otomatik
görüyor ama pahalı, üçüncüsü ucuz kalıyor ama görmesi için birinin `yenile()`'yi çağırması
gerekiyor. Kim çağıracak sorusu bu dersin sınırları dışında — gerçek bir sistemde bu
çağrıyı bir zamanlayıcı, bir dış sinyal ya da bir sonraki istek tetikleyebilir; burada
önemli olan, çağrı gelmedikçe hiçbir şeyin kendiliğinden değişmediği.

#### Sessiz Kusur: Aynı Anda İki Farklı Gerçek

Önceki iki bölümün gösterdiği fark bir sırayla ilgiliydi — önce ve sonra. Bu bölüm aynı
sorunu **eşzamanlılıkla** soruyor: bir uygulamanın birden çok bileşeni varken, hepsi aynı
merkezî değere aynı anda mı bakıyor?

```java
// SessizFark.java — ayni anda var olan uc bilesen, disaridaki tek bir degere iki farkli yanit veriyor
import java.util.*;

public class SessizFark {
    public static void main(String[] args) {
        MerkeziAyarlar.guncelle("mod", "normal");
        TekilBilesen eskiTekil = new TekilBilesen();

        MerkeziAyarlar.guncelle("mod", "bakim");
        HerIstekteBilesen yeniIstek1 = new HerIstekteBilesen();
        HerIstekteBilesen yeniIstek2 = new HerIstekteBilesen();

        Olcum.goster("eskiTekil.mod()", eskiTekil.mod());
        Olcum.goster("yeniIstek1.mod()", yeniIstek1.mod());
        Olcum.goster("yeniIstek2.mod()", yeniIstek2.mod());

        long farkliGorusSayisi = new HashSet<>(List.of(eskiTekil.mod(), yeniIstek1.mod(), yeniIstek2.mod())).size();
        Olcum.goster("bu ucunden herhangi biri hata verdi mi", "hayir");
        Olcum.goster("ayni anda gozlenen farkli deger sayisi", farkliGorusSayisi);
    }
}
```

```
eskiTekil.mod()                                     : normal
yeniIstek1.mod()                                    : bakim
yeniIstek2.mod()                                    : bakim
bu ucunden herhangi biri hata verdi mi              : hayir
ayni anda gozlenen farkli deger sayisi              : 2
```

`eskiTekil` bilinçli olarak değişiklikten **önce** kuruluyor, `yeniIstek1` ile `yeniIstek2`
değişiklikten **sonra** — sıralama koşumda açıkça yazılı, çünkü gerçek bir uygulamada hangi
bileşenin ne zaman kurulduğu genellikle bu kadar net görünmez; istekler saniyeler, hatta
milisaniyeler arayla gelir ve aralarına sıkışan bir değişiklik hiçbir zaman fark edilmeyebilir. Üç
çağrı da başarıyla dönüyor, üçü de bir değer veriyor, hiçbiri istisna fırlatmıyor. **GT26.** Aynı
anda var olan üç bileşen dışarıdaki aynı anahtara iki farklı yanıt verebilir, üçü de hatasız:
`eskiTekil` uygulamanın normal modda olduğunu söylerken, `yeniIstek1` ve `yeniIstek2` bakım
modunda olduğunu söylüyor — üçü de aynı `mod` anahtarına bakıyor, üçü de "doğru" cevap verdiğinden
emin. **GT27.** Bu fark hiçbir yerde hata olarak görünmez; günlüğe hiçbir uyarı düşmez, hiçbir
istisna fırlamaz, çünkü her bileşen kendi okuduğu değere göre tutarlı davranıyor. Fark ancak
`eskiTekil.mod()` ile `yeniIstek1.mod()` yan yana konduğunda, dışarıdan bakan biri ikisini
karşılaştırdığında görünüyor — kod içinde bu karşılaştırmayı kimse yapmıyor, çünkü `eskiTekil` ile
`yeniIstek1` birbirinin varlığından habersiz. Kursun daha önceki sessiz kusurları (yanlış yazılmış
ayar adı, yanlış işaretlenmiş ölçüm alanı) en azından tek bir bileşenin içindeydi; bu kusur
bileşenler **arasında** ve hiçbir tek bileşenin kaynağı bakılarak görülemez.

Üç bileşen türünün dışarıdaki değişikliğe verdiği yanıt tek tabloda özetlenebilir:

| Bileşen türü | Değişiklikten sonra ne görüyor | Değişikliği ne zaman görüyor |
|---|---|---|
| `TekilBilesen` | eski değer, sonsuza kadar | asla |
| `HerIstekteBilesen` | güncel değer | bir sonraki kurulumda |
| `YenilenebilirBilesen` | eski değer, `yenile()` çağrılana kadar | yalnız çağrıldığında |

Tablonun hiçbir satırı "yanlış" değil — üçü de kendi kuralına göre tutarlı çalışıyor.
Sorun kuralın kendisinde değil, aynı uygulamanın farklı bileşenlerinin farklı kurallarla
kurulmuş olabilmesinde: `eskiTekil` ile `yeniIstek1` aynı `MerkeziAyarlar`'a bakıyor, ikisi
de doğru çalışıyor ve yine de birbirinden farklı bir gerçeği söylüyorlar.

#### Özet

- Merkezî yapılandırma dışarıda tutulur; bir bileşenin bu değeri ne zaman gördüğü kendi
  kapsamına bağlıdır.
- Tekil bir bileşen değeri yalnız kurulduğu anda okur; dışarıdaki değişiklik o örneğe hiç
  ulaşmaz.
- Her istekte yeniden kurulan bir bileşen değeri her seferinde yeniden okur; değişiklik bir
  sonraki kurulumda görünür.
- Yenileme kendiliğinden olmaz, açık bir çağrı gerektirir; çağrılmadıkça tekil bileşen eski
  değeri tutmaya devam eder.
- Aynı anda var olan iki bileşen dışarıdaki aynı değere iki farklı, ikisi de hatasız yanıt
  verebilir; fark yalnız yan yana karşılaştırıldığında görünür.

#### Sonraki Adım

Bu ders bir değerin hangi bileşene ulaştığını ölçtü; sıradaki ders bir çağrının hangi
hedefe ulaştığını soruyor. Bir bileşen başka bir bileşeni çağırmak istediğinde o hedefi
nereden buluyor — kaynakta yazılı bir adresten mi, bir kayıttan mı, yoksa kabın kendi
çözümünden mi? Gerçek bir ağ çağrısı yok, ama hedefin nasıl belirlendiği yine ölçülebilir.

### Servis Keşfi ve Ağ Geçidi

Source: https://academia.sh/tr/kurslar/kurumsal-uygulama-cercevesi/servis-kesfi-ve-ag-gecidi

Bir çağrının hedefini kim belirledi — kaynaktaki sabit bir referans mı, bir kayıt mı, yoksa kabın kendi varsayılan çözümü mü? Keşif ve yönlendirme modellenir, gerçek ağ çağrısı yoktur. Sınırlayıcı ölçüm, keşfin bir hedefi bulmakla o hedefin hâlâ yanıt verip vermediğini doğrulamak arasındaki farkı ölçer — kayıttan silinmemiş ölü bir hedef aramada aynı biçimde bulunur, kusur ancak çağrı anında düşer.

Önceki ders bir değerin hangi bileşene ulaştığını ölçtü. Bu ders bir çağrının hangi hedefe
ulaştığını soruyor. Bir bileşen başka bir bileşeni çağırmak istediğinde, o hedefi nereden buluyor?
Üç yol modelleniyor: kaynakta sabit yazılı doğrudan bir referans, bir kayıttan okunan bir hedef ve
kayıtta hiçbir şey bulunmadığında devreye giren kabın kendi varsayılan çözümü. Servis keşfinin ve
ağ geçidinin kavramı M16/K06 Servis Mimarileri kursunda kuruldu ve burada tekrarlanmıyor; bu ders
yalnız hedefin kaynakta nereden geldiğini ölçüyor. Gerçek bir ağ çağrısı yok — `Servis` arayüzünü
gerçekleştiren birkaç sınıf, doğrudan Java çağrılarıyla birbirine bağlanıyor, ama sorulan soru
gerçek bir dağıtık sistemdekiyle aynı: hedefi kim seçti ve o hedef hâlâ orada mı?

Gerçek bir dağıtık sistemde bir hedefin "adresi" bir ana makine adı ve bir bağlantı
noktasıdır ve bu ikisi zamanla değişebilir — bir kopya kapanır, yenisi başka bir adreste
açılır. Bu ders bu değişkenliği bir ana makine adıyla değil bir Java nesnesiyle modelliyor:
`ServisKaydi`'nin tuttuğu şey bir ağ adresi değil, doğrudan bir `Servis` örneği, ama sorduğu
soru aynı — bir ad, hangi somut hedefe karşılık geliyor ve bu karşılık zamanla
değişebiliyor mu?

#### Hedefi Kim Belirledi: Doğrudan Referans, Kayıt, Varsayılan

Dört sınıf aynı `Servis` arayüzünü gerçekleştiriyor: `EnvanterServisiV1`,
`EnvanterServisiV2`, `YedekServis` ve ilerleyen bölümde kullanılacak `OluServis`. Bir
`ServisKaydi`, adları servis örneklerine eşliyor; bir `AgGecidi`, bir ad verildiğinde önce
kayda bakıyor, kayıtta yoksa kendi varsayılan servisine dönüyor.

```java
// Servis.java — cagrilabilecek her hedefin ortak arayuzu (yardimci kaynak, main yok)
interface Servis { String yanitVer(); }
```

```java
// EnvanterServisiV1.java — bir hedef gerceklestirimi (yardimci kaynak, main yok)
class EnvanterServisiV1 implements Servis {
    public String yanitVer() { return "envanter-v1"; }
}
```

```java
// EnvanterServisiV2.java — ayni mantiksal adin ikinci bir gerceklestirimi (yardimci kaynak, main yok)
class EnvanterServisiV2 implements Servis {
    public String yanitVer() { return "envanter-v2"; }
}
```

```java
// YedekServis.java — kayitta karsiligi olmayan her hedef icin kabin varsayilan cozumu (yardimci kaynak, main yok)
class YedekServis implements Servis {
    public String yanitVer() { return "yedek"; }
}
```

```java
// ServisKaydi.java — adlari servis orneklerine eslen kayit (yardimci kaynak, main yok)
import java.util.*;

class ServisKaydi {
    final Map<String, Servis> kayitlar = new HashMap<>();
    void kaydet(String ad, Servis s) { kayitlar.put(ad, s); }
    Servis bul(String ad) { return kayitlar.get(ad); }
}
```

```java
// AgGecidi.java — bir ad verildiginde once kayda bakan, bulamazsa varsayilana donen yonlendirici (yardimci kaynak, main yok)
class AgGecidi {
    final ServisKaydi kayit;
    final Servis varsayilanServis;

    AgGecidi(ServisKaydi kayit, Servis varsayilanServis) {
        this.kayit = kayit;
        this.varsayilanServis = varsayilanServis;
    }

    String cagir(String hedefAdi) {
        Servis s = kayit.bul(hedefAdi);
        return (s != null ? s : varsayilanServis).yanitVer();
    }
}
```

```java
// Olcum.java — sonuc yazdirma yardimcisi (yardimci kaynak, main yok)
class Olcum {
    static void goster(String etiket, Object deger) {
        System.out.printf("%-52s: %s%n", etiket, deger);
    }
}
```

```java
// HedefiKimBelirledi.java — ayni cagri seklinin uc ayri kaynaktan hedef aldigi gosteriliyor
public class HedefiKimBelirledi {
    public static void main(String[] args) {
        Servis dogrudan = new EnvanterServisiV1();
        Olcum.goster("dogrudan referans ile cagri", dogrudan.yanitVer());

        ServisKaydi kayit = new ServisKaydi();
        kayit.kaydet("envanter", new EnvanterServisiV2());
        AgGecidi agGecidi = new AgGecidi(kayit, new YedekServis());

        Olcum.goster("ag gecidi uzerinden, kayittaki hedef", agGecidi.cagir("envanter"));
        Olcum.goster("ag gecidi uzerinden, kayitta olmayan hedef", agGecidi.cagir("fatura"));
    }
}
```

```
dogrudan referans ile cagri                         : envanter-v1
ag gecidi uzerinden, kayittaki hedef                : envanter-v2
ag gecidi uzerinden, kayitta olmayan hedef          : yedek
```

**GT28.** Doğrudan bir referansla yapılan çağrının hedefi kaynakta sabit yazılıdır — bu bir
çağrı: `new EnvanterServisiV1()` satırı hangi gerçekleştirimin kullanılacağını doğrudan
belirliyor ve bu karar `HedefiKimBelirledi` sınıfı derlendiği anda sabitleniyor. **GT29.**
Ağ geçidi üzerinden yapılan çağrının hedefi kayıttan gelir; kaynakta sabit olan yalnız bir
ad — `"envanter"` — hangi nesnenin o adı karşıladığı değil. `agGecidi.cagir("envanter")`
satırı hiç değişmeden, kayda konan nesne `EnvanterServisiV1`'den `EnvanterServisiV2`'ye
değişse bile aynı çağrı farklı bir gerçekleştirime ulaşır. **GT30.** Kayıtta karşılığı
olmayan bir ad istendiğinde ağ geçidi kabın kendi varsayılan servisine döner; bu üçüncü yol
kaynakta hiç görünmez — `"fatura"` adının kayıtlı olmadığı `AgGecidi.cagir` yönteminin içine
bakmadan anlaşılamaz.

Doğrudan referans en hızlı yol, ama en katı: `EnvanterServisiV1`'i `EnvanterServisiV2`'ye
değiştirmek `HedefiKimBelirledi` sınıfının kendisini değiştirip yeniden derlemeyi
gerektiriyor. Kayıt dolaylı bir katman ekliyor — çağıran taraf yalnız bir ad biliyor,
hedefin hangi sınıftan geldiğini hiç bilmiyor — ve bu dolaylılığın bedeli, hedefi bulmanın
artık bir harita araması gerektirmesi. Varsayılan servis üçüncü bir seçenek sunuyor: kayıt
eksik olsa bile çağıran taraf bir istisna almak yerine bir yanıt alıyor, karşılığında o
yanıtın gerçek hedeften mi yoksa yedekten mi geldiğini kaynaktan anlamak imkânsız hâle
geliyor.

Üç yolun ortak noktası, hiçbirinin diğerinden "daha doğru" olmaması. `dogrudan.yanitVer()`
çağrısı hiçbir dolaylılık taşımadığı için en hızlı ve en öngörülebilir, ama değişikliğe en
kapalı olan da bu — kaynak metni değiştirmeden hedefi değiştirmenin hiçbir yolu yok. Kayıt
üzerinden çağrı bu katılığı kaldırıyor: `ServisKaydi` içeriği koşum sırasında değişebilir
ve `agGecidi.cagir("envanter")` satırı bir kez yazılıp bir daha hiç değişmeden farklı
hedeflere yönlendirilebilir. Varsayılan servis ise bir üçüncü ödünleşim sunuyor — hiçbir
kayıt olmasa bile çağıran taraf boş elle dönmüyor, ama bu rahatlığın bedeli, "yanıt geldi"
bilgisinin "doğru hedeften yanıt geldi" bilgisiyle aynı şey olmaması.

#### Sınırlayıcı Ölçüm: Keşif Hedefi Bulur, Doğrulamaz

`ServisKaydi.bul` yönteminin tek işi bir adı bir nesneye çevirmek; o nesnenin hâlâ doğru
çalışıp çalışmadığına dair hiçbir bilgisi yok. Kayıt sınıfının kendi kaynağına bakıldığında
bu açıkça görülüyor: `kayitlar.get(ad)` bir harita araması ve bir haritadan okumak hiçbir
zaman elindeki nesneyi çağırmaz — yalnız var olup olmadığına bakar. Bunu görmek için kayda
bilinçli olarak bozuk bir hedef ekleniyor — `OluServis`, çağrıldığında her zaman bir istisna
fırlatan bir gerçekleştirim, ama kayıttan hiç silinmemiş; kayıt açısından `fatura` adı hâlâ
geçerli bir kayıt, tıpkı `envanter` gibi.

```java
// OluServis.java — kayitta duran ama cagrildiginda her zaman basarisiz olan bir hedef (yardimci kaynak, main yok)
class OluServis implements Servis {
    public String yanitVer() { throw new IllegalStateException("hedefe ulasilamiyor"); }
}
```

```java
// KesifDogrulamaz.java — kayit hem saglikli hem olu hedefi ayni bicimde "buldum" diyor, fark yalnizca cagri aninda cikiyor
public class KesifDogrulamaz {
    public static void main(String[] args) {
        ServisKaydi kayit = new ServisKaydi();
        kayit.kaydet("envanter", new EnvanterServisiV1());
        kayit.kaydet("fatura", new OluServis());

        Olcum.goster("kayit /envanter hedefini buldu mu", kayit.bul("envanter") != null);
        Olcum.goster("kayit /fatura hedefini buldu mu (hedef aslinda olu)", kayit.bul("fatura") != null);

        AgGecidi agGecidi = new AgGecidi(kayit, new YedekServis());
        try {
            agGecidi.cagir("fatura");
            Olcum.goster("cagri basarili mi", "evet");
        } catch (IllegalStateException e) {
            Olcum.goster("cagri basarili mi", "hayir, " + e.getClass().getSimpleName() + " cagri aninda dustu");
        }
    }
}
```

```
kayit /envanter hedefini buldu mu                   : true
kayit /fatura hedefini buldu mu (hedef aslinda olu) : true
cagri basarili mi                                   : hayir, IllegalStateException cagri aninda dustu
```

İlk iki satır birebir aynı: `kayit.bul` hem sağlıklı hem ölü hedef için `true` döndürüyor,
çünkü ikisi de haritada bir anahtara karşılık geliyor — kayıt yalnız bir eşlemeyi kontrol
ediyor, hedefin canlılığını değil. **GT31.** Keşif bir hedefi bulur ama onun hâlâ yanıt
verip vermediğini doğrulamaz; kayıttan silinmemiş ölü bir hedef aramada sağlıklı bir hedefle
aynı biçimde bulunur, kusur ancak çağrı anında düşer. `AgGecidi.cagir` çağrısı üçüncü
satırda `IllegalStateException` fırlatana kadar hiçbir şey `fatura` hedefinin bozuk
olduğunu söylemiyor — ne kayıt, ne ağ geçidi, ne de bir önceki `bul` çağrısı. Bu, kursun
başka bir sessiz-öncesi kusuru: keşif adımı başarıyla geçiyor, kusur bir adım sonra, hiç
beklenmeyen bir anda düşüyor.

Bu ayrım pratikte önemli, çünkü bir sistemin "sağlıklı" göründüğü an ile "gerçekten
çalıştığı" an aynı an olmayabiliyor. `ServisKaydi` gibi bir yapı yalnız kayıtlı adları
listeleyebiliyor; kayıtlı bir adın arkasındaki hedefin hâlâ ayakta olup olmadığını sormak
tamamen ayrı bir sorumluluk ve bu ders o sorumluluğun `bul` yönteminde **bulunmadığını**
gösteriyor. Bu boşluğu kapatmanın bir yolu, kaydı canlılık bilgisiyle birlikte tutmak —
her hedefin yanına bir "son yanıt verme" bilgisi eklemek ve `bul` yerine yalnız bu bilgisi
güncel olan hedefleri döndüren ayrı bir yöntem yazmak. Bu ders o yapıyı kurmuyor; yalnız
onun **eksikliğinin** sonucunu ölçüyor, çünkü sınırlayıcı ölçümün amacı düzeltmeyi
göstermek değil, düzeltilmediğinde ne olduğunu saymak.

Üç bölümün ölçtüğü şey tek tabloda toplanabilir:

| Soru | Yanıt | Kaynaktaki karşılığı |
|---|---|---|
| Hedefi kim belirledi (doğrudan referans) | `EnvanterServisiV1` | `new EnvanterServisiV1()` — çağrı |
| Hedefi kim belirledi (ağ geçidi) | kayıttaki neyse o | `"envanter"` adı — kayıt |
| Kayıtta olmayan ad istenirse | `YedekServis` | hiçbir satır — varsayılan |
| `bul("fatura")` hedefi buluyor mu | evet, `true` | keşif yalnız eşlemeye bakar |
| `cagir("fatura")` başarılı mı | hayır, istisna | canlılık yalnız çağrı anında sınanır |

Tablonun son iki satırı aynı hedef için farklı sonuç veriyor — bu, dersin özeti aslında:
bir hedefin **var olması** ile o hedefin **çalışıyor olması** iki ayrı sorudur ve keşif
yalnız birincisine yanıt verir.

#### Özet

- Doğrudan bir referansla yapılan çağrının hedefi kaynakta sabit yazılıdır; bu, davranışın
  kaynağı çağrı olan durumdur.
- Ağ geçidi üzerinden yapılan çağrının hedefi bir kayıttan gelir; kaynakta sabit olan
  yalnız bir ad, hangi nesnenin o adı karşıladığı değil.
- Kayıtta karşılığı olmayan bir ad istendiğinde ağ geçidi kabın kendi varsayılan servisine
  döner; bu yol kaynakta hiç görünmez.
- Keşif bir hedefi bulur ama onun hâlâ yanıt verip vermediğini doğrulamaz; sağlıklı ve ölü
  hedef aramada aynı biçimde bulunur.
- Kayıttan silinmemiş ölü bir hedefin kusuru keşif anında değil, çağrı anında düşer.

#### Sonraki Adım

Bu ders bir çağrının hedefe ulaşıp ulaşamadığını, ulaştıysa ne zaman başarısız olduğunu
ölçtü. Sıradaki ders bu başarısızlığı bir veri noktası değil, tekrar eden bir olay olarak
ele alıyor: bir hedef arka arkaya kaç kez başarısız olursa çağıran taraf denemeyi bırakmalı
ve bu karar nereden geliyor? Kurs ve müfredat kapanışı bu son sınırı ölçüyor.

### Dayanıklılık Kalıpları

Source: https://academia.sh/tr/kurslar/kurumsal-uygulama-cercevesi/dayaniklilik-kaliplari

Devre kesici ve yeniden deneme bir açıklamayla ekleniyor; çağrı yeri hiç değişmiyor. Kurgulanmış bir başarısızlıkta kaç gerçek deneme yapıldığı ve devrenin hangi eşikte açıldığı sayı olarak ölçülüyor. Sınırlayıcı ölçüm, açıklamanın yalnız vekilden geçen çağrıya uygulandığını, aynı sınıfın kendi içinden yaptığı çağrının korumasız kaldığını gösteriyor — web-and-data-layer/06'nın ölçtüğü sınırın aynısı, başka bir sonuçla. Bu ders kursu ve M08 müfredatını kapatıyor.

Önceki ders bir çağrının hedefe ulaşıp ulaşamadığını ölçtü. Bu ders bu başarısızlığı tek
bir olay değil, tekrar eden bir örüntü olarak ele alıyor: bir hedef arka arkaya başarısız
olduğunda çağıran taraf ne yapmalı — hemen mi vazgeçmeli, birkaç kez mi denemeli, yoksa bir
noktadan sonra denemeyi tümüyle mi kesmeli? Devre kesici ve yeniden deneme kalıplarının
kavramı M19/K05 Dayanıklılık ve Güvenilirlik kursunda kuruldu ve burada tekrarlanmıyor; bu
ders yalnız bu kalıpların bir açıklamayla nasıl eklendiğini ve kaynakta ne bıraktığını
ölçüyor. Gerçek bir süre yazılmıyor — ölçülen şey deneme sayısı ve eşik, ikisi de tam sayı.
Bu, kursun ve M08 müfredatının kapanış dersi.

#### Açıklamayla Eklenen Dayanıklılık: Deneme Sayısı ve Eşik

`KirilganServis`, her çağrıldığında başarısız olacak şekilde kurgulanmış bir hedef; üzerinde
iki açıklama taşıyor — `@YenidenDene(sayisi = 3)` ve `@DevreKesici(esik = 5)`. Bu iki işareti
okuyup uygulayan taraf `KirilganServis`'in kendisi değil, onu saran `DayaniklilikVekili`.

```java
// YenidenDene.java — bir hedefin kac kez tekrar denenecegini belirten aciklama tipi (yardimci kaynak, main yok)
import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE)
@interface YenidenDene { int sayisi(); }
```

```java
// DevreKesici.java — ard arda kac basarisizliktan sonra devrenin acilacagini belirten aciklama tipi (yardimci kaynak, main yok)
import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE)
@interface DevreKesici { int esik(); }
```

```java
// DisServis.java — cagrilan hedefin arayuzu (yardimci kaynak, main yok)
interface DisServis { String cagir(); }
```

```java
// KirilganServis.java — her cagrildiginda basarisiz olacak sekilde kurgulanmis hedef (yardimci kaynak, main yok)
@YenidenDene(sayisi = 3) @DevreKesici(esik = 5)
class KirilganServis implements DisServis {
    int cagriSayaci = 0;

    public String cagir() {
        cagriSayaci++;
        throw new RuntimeException("hedef basarisiz");
    }

    String digerIslem() {
        // kendi icinden dogrudan cagiriyor, vekili hic gormuyor
        return cagir();
    }
}
```

```java
// DayaniklilikVekili.java — aciklamalari okuyup yeniden deneme ve devre kesici davranisini uygulayan vekil (yardimci kaynak, main yok)
class DayaniklilikVekili implements DisServis {
    final DisServis hedef;
    final int denemeSayisi;
    final int esik;
    int ardArdaBasarisizlik = 0;
    boolean devreAcik = false;
    int gercekCagriSayaci = 0;

    DayaniklilikVekili(DisServis hedef) {
        this.hedef = hedef;
        Class<?> tip = hedef.getClass();
        YenidenDene yd = tip.getAnnotation(YenidenDene.class);
        DevreKesici dk = tip.getAnnotation(DevreKesici.class);
        this.denemeSayisi = yd != null ? yd.sayisi() : 1;
        this.esik = dk != null ? dk.esik() : Integer.MAX_VALUE;
    }

    public String cagir() {
        if (devreAcik) {
            throw new IllegalStateException("devre acik, hedefe hic ulasilmadi");
        }
        RuntimeException sonHata = null;
        for (int i = 1; i <= denemeSayisi; i++) {
            gercekCagriSayaci++;
            try {
                String sonuc = hedef.cagir();
                ardArdaBasarisizlik = 0;
                return sonuc;
            } catch (RuntimeException e) {
                sonHata = e;
                ardArdaBasarisizlik++;
                if (ardArdaBasarisizlik >= esik) { devreAcik = true; break; }
            }
        }
        throw sonHata;
    }
}
```

```java
// Olcum.java — sonuc yazdirma yardimcisi (yardimci kaynak, main yok)
class Olcum {
    static void goster(String etiket, Object deger) {
        System.out.printf("%-52s: %s%n", etiket, deger);
    }
}
```

`KirilganServis.cagir` yönteminin gövdesinde ne bir deneme sayacı ne bir eşik karşılaştırması
var; ikisi de `DayaniklilikVekili` tarafında. Aynı `vekil.cagir()` satırı üç kez art arda
çağrılıyor.

```java
// DenemeVeEsik.java — ayni cagri satiri, kurgulanmis surekli basarisizlikta gercek deneme sayisini ve devrenin actigi esigi ortaya cikariyor
public class DenemeVeEsik {
    public static void main(String[] args) {
        DayaniklilikVekili vekil = new DayaniklilikVekili(new KirilganServis());

        denemeYap(vekil, "1. cagri");
        denemeYap(vekil, "2. cagri");
        denemeYap(vekil, "3. cagri");

        Olcum.goster("toplam gercek deneme sayisi (hedefe ulasan)", vekil.gercekCagriSayaci);
        Olcum.goster("devre acik mi", vekil.devreAcik);
    }

    static void denemeYap(DayaniklilikVekili vekil, String etiket) {
        try {
            vekil.cagir();
            Olcum.goster(etiket, "basarili");
        } catch (Exception e) {
            Olcum.goster(etiket, e.getMessage());
        }
    }
}
```

```
1. cagri                                            : hedef basarisiz
2. cagri                                            : hedef basarisiz
3. cagri                                            : devre acik, hedefe hic ulasilmadi
toplam gercek deneme sayisi (hedefe ulasan)         : 5
devre acik mi                                       : true
```

**GT32.** Devre kesici ve yeniden deneme bir açıklamayla ekleniyor; `vekil.cagir()` çağrı
satırı üç `denemeYap` çağrısı boyunca hiç değişmiyor, davranışı belirleyen tek şey
`KirilganServis` üzerindeki iki işaret. **GT33.** Kurgulanmış sürekli başarısızlıkta gerçek
deneme sayısı ve devrenin açıldığı eşik, hepsi sayı: ilk çağrı üç deneme yapıyor (`sayisi =
3`), henüz eşiğe (`esik = 5`) ulaşmadığı için devre kapalı kalıyor; ikinci çağrı iki deneme
daha yapıyor ve ard arda başarısızlık beşe ulaştığı anda devre açılıyor — toplam gerçek
deneme sayısı tam **5**. **GT34.** Devre açıldıktan sonraki üçüncü çağrı hedefe hiç
ulaşmadan reddediliyor; `gercekCagriSayaci` üçüncü çağrıdan sonra hâlâ 5, artmıyor.

#### Sınırlayıcı Ölçüm: Vekilden Geçmeyen Çağrı Korumasız Kalır

`KirilganServis`'in kendi içinde `cagir`'i çağıran ikinci bir yöntemi var: `digerIslem`.
Bu çağrı `DayaniklilikVekili`'nden hiç geçmiyor — nesnenin kendisi kendi yöntemini doğrudan
çağırıyor.

```java
// KorumasizCagri.java — digerIslem() kendi icinden dogrudan cagir()'i cagiriyor, vekil hic devrede degil, hicbir yeniden deneme olmuyor
public class KorumasizCagri {
    public static void main(String[] args) {
        KirilganServis ic = new KirilganServis();
        try {
            ic.digerIslem();
        } catch (RuntimeException e) {
            Olcum.goster("digerIslem() cagrisinin sonucu", e.getMessage());
        }
        Olcum.goster("digerIslem icinden yapilan dogrudan cagri, kac deneme yapildi", ic.cagriSayaci);
    }
}
```

```
digerIslem() cagrisinin sonucu                      : hedef basarisiz
digerIslem icinden yapilan dogrudan cagri, kac deneme yapildi: 1
```

`ic.cagriSayaci` bir kez artıyor, üç değil. **GT35.** Açıklama yalnız vekilden geçen çağrıya
uygulanır; aynı sınıfın kendi içinden yaptığı çağrı korumasız kalır, çünkü `@YenidenDene` ve
`@DevreKesici` işaretlerini okuyan taraf `DayaniklilikVekili` ve `digerIslem` onu hiç
çağırmıyor. Bu, web-and-data-layer/06'nın ölçtüğü sınırın aynısı — bir bileşenin kendi
kendini çağırması vekili atlıyordu — burada aynı sınır başka bir sonuçla karşımıza çıkıyor:
orada sarmalanan bir işlem sınırı hiç açılmıyordu, burada sarmalanan bir dayanıklılık
kalıbı hiç uygulanmıyor. Kod okuyan biri `digerIslem`'in de aynı korumaya sahip olduğunu
varsayabilir, çünkü aynı sınıfın aynı `cagir` yöntemini çağırıyor; kaynakta bu varsayımı
çürütecek hiçbir işaret yok.

#### Özet

- Devre kesici ve yeniden deneme bir açıklamayla eklenir; çağrı yeri hiç değişmez.
- Kurgulanmış sürekli başarısızlıkta gerçek deneme sayısı ve devrenin açıldığı eşik sayı
  olarak ölçülür, hiçbiri süre değildir.
- Devre açıldıktan sonraki çağrılar hedefe hiç ulaşmadan reddedilir.
- Açıklama yalnız vekilden geçen çağrıya uygulanır; aynı sınıfın kendi içinden yaptığı çağrı
  korumasız kalır.
- Bu sınır, web-and-data-layer/06'nın ölçtüğü kendi-kendini-çağırma sınırının aynısı, başka
  bir sonuçla.

#### Kurs Kapanışı

Kurs tek bir soruyla açıldı: bir uygulama çerçevesi kullanan programın büyük bölümünü hiç
kimse çağırmaz, çünkü nesneyi kuran ifade, bağımlılığı veren atama, kurulumu çağıran satır
yerlerinde yalnız açıklamalar durur. On dokuz ders bu soruyu üç konuda sordu — çekirdek
kavramlar, web ve veri katmanı, güvenlik ve dağıtık kurulum — ve her seferinde aynı üçlüye
döndü: bu davranışı kaynakta bir çağrı mı doğurdu, bir açıklama mı, yoksa hiçbiri mi.

| Ders | Ölçülen davranış | Davranışın kaynağı | Sınırlayıcı ölçüm |
|---|---|---|---|
| Denetimin Ters Çevrilmesi | 12 davranışın 3'ü çağrı, 6'sı açıklama, 3'ü varsayılan | çağrı · açıklama · varsayılan | Kabın dışında kalan nesne açıklamaların hiçbirini almaz |
| Bağımlılık Enjeksiyonu | Aynı `Servis` hem kapla hem elle kurulup ikisi de çalışıyor | açıklama (sağlama) · çağrı (elle verme) | Bağlanmamış arayüz hatası kap kurulurken değil ilk istekte düşer |
| Bileşen Kapsamları ve Yaşam Döngüsü | Tekil kapsam varsayılan, her-istekte açık bir açıklama | varsayılan (tekil) · açıklama (her-istekte) | Tanınmayan kapsam adı hata vermez, bileşen sessizce tekil olmaktan çıkar |
| Yapılandırma Kaynakları | 4 ayardan 1'i dış kaynaktan, 2'si dosyadan, 1'i gömülü varsayılandan | kabın kod sırası (öncelik zinciri) | Yazım farkı (ı/i) hiçbir hata vermeden gömülü varsayılana düşürür |
| Otomatik Yapılandırma | Açıklamasız doğan davranış sayısı 3'ten 2'ye düşüyor | varsayılan (koşullu bileşen seçimi) | Bir bileşen eklemek başka birini sessizce devre dışı bırakır |
| Görünüş Odaklı Programlama | Sarmalama açıklaması çağrı yerine dokunmadan sayaç ekliyor | açıklama (vekil aracılığıyla) | Arayüzsüz sınıfa konan işaret hata vermeden etkisiz kalır |
| Gömülü Sunucu ve İstek İşleme Modeli | 6 halkanın 2'si varsayılan, 2'si açıklama, 2'si çağrı | varsayılan · açıklama · çağrı | Elle çağrılan sınıflarda açıklamadaki öncelik hiç okunmaz |
| Denetleyiciler ve Yönlendirme | 4 uç noktanın 4'ü de açıklamadan doğuyor | açıklama (`@UcNokta`) | Eşleşmeyen yol sessizce varsayılan yanıt döner; çakışan yolda kazanan tarama sırasına bağlı |
| Doğrulama ve Hata İşleme | 4 alanın 3'ü açıklamadan kural alıyor, 1'i hiç kural taşımıyor | açıklama (kural) · varsayılan (zarf) | Kap dışında işleyiciye verilen geçersiz veri hiç doğrulanmaz, "tamam" döner |
| Veri Erişim Soyutlaması | Aynı derlenmiş çağıran iki gerçekleştirimle, yeniden derlenmeden koşuyor | çağıran yalnız arayüzü anıyor | `DiziDepo` kapasite sınırında istisna verir, `BellekDepo` aynı çağrıda vermez |
| Nesne ve Tablo Bağlaması | 4 alanın 2'si yükleme anında, 2'si ilk erişimde yükleniyor | açıklama (`@Sutun` / `@TembelSutun`) | Kaptan ayrılan nesnede erişilmemiş tembel alan sessizce boş döner |
| İşlem Yönetimi | Dıştan gelen her çağrıda sınır açılıyor, iç çağrı hiç sayılmıyor | açıklama (`@Islem`, vekil üzerinden) | Bileşenin kendi kendini çağırması vekili atlar, sınır hiç açılmaz |
| Kimlik Doğrulama ve Yetkilendirme | Yetki kararı açıklamadan (panel) ya da varsayılandan (durum) geliyor | açıklama · varsayılan · çağrı (elle) | Aynı kaynak iki farklı varsayılanla iki farklı güvenlik duruşu üretir |
| Belirteç Tabanlı Kimlik Doğrulama | İddia sayısı 1/3; doğrulama açıklama ve çağrıyla aynı sonucu veriyor | açıklama (`@GerekliIddia`) · çağrı (elle) | İptal edilen belirteç, iptal listesine hiç bakılmadığı için süresi dolana kadar geçerli kalır |
| Katman Testleri | Dilim testi 2, tam bağlam testi 4 bileşen kuruyor | kabın kurduğu bileşen sayısı | Dilimin dışında kalan bileşenin kurulum kancası hiç çalışmasa da test yine geçer |
| Sağlık ve Ölçüm Uçları | 2 uç varsayılandan, 1 uç açıklamadan; 3 alandan 1'i dışarı çıkıyor | varsayılan (2 uç) · açıklama (1 uç) | Yanlış işaretlenen alan da hiçbir hata vermeden dışarı verilir |
| Dağıtık Yapılandırma | Tekil bileşen eski değeri, her istekte kurulan güncel değeri görüyor | varsayılan (kapsam) · çağrı (`yenile()`) | Aynı anda iki bileşen aynı değere iki farklı, ikisi de hatasız yanıt verir |
| Servis Keşfi ve Ağ Geçidi | Hedef doğrudan referanstan, kayıttan, ya da kabın varsayılanından geliyor | çağrı · kayıt · varsayılan | Kayıttan silinmemiş ölü hedef aramada bulunur, kusur çağrı anında düşer |
| Dayanıklılık Kalıpları | Gerçek deneme sayısı 5, devre eşik 5'te açılıyor | açıklama (`@YenidenDene`, `@DevreKesici`) | Vekilden geçmeyen kendi-kendini-çağırma korumasız kalır |

Tablonun üçüncü sütunu tek bir örüntüyü doğruluyor: on dokuz dersin beşinde davranış tamamen
açıklamadan (görünüş sarmalaması, dört uç nokta, sütun bağlaması, işlem sınırı, dayanıklılık
kalıpları), üçünde tamamen ya da ağırlıkla varsayılandan doğuyor, geri kalanında üçü de bir arada
görünüyor. Sınırlayıcı ölçüm sütunu ise başka bir örüntü taşıyor: on dokuz dersin neredeyse
tamamında kusur **sessiz** — hata fırlatan yalnız ikisi (bağlanmamış arayüz, kapasite sınırı),
geri kalanı ya beklenmedik bir değer döndürüyor ya da hiçbir şey değişmiyormuş gibi devam ediyor.
Kaynağı yukarıdan aşağı okuyan biri, bu on dokuz dersin gösterdiği davranışın büyük bölümünü
göremiyor ve göremediğinin bozulduğunu da göremiyor.

M08 Java müfredatının altı kursu birbirinden ayrı altı soru sordu ve hiçbiri ötekinin
yerini almadı. M08/K01 Java Temelleri, derleyicinin kaynakta yazılmayan hangi adımı sınıf
dosyasına eklediğini saydı. M08/K02 Nesneye Dayalı Java, çalışma zamanındaki bir kararı
hangi tipin verdiğini ayırdı. M08/K03 Standart Kütüphane ve Akışlar, bir garantiyi kimin —
çağıranın mı, kütüphanenin mi — verdiğini ölçtü. M08/K04 JVM, Eşzamanlılık ve Başarım, bir
adımı hangi iş parçacığının koşturduğunu izledi. M08/K05 Derleme, Test ve Ekosistem, bir
tipin hangi sınıf yolunda bulunmak zorunda olduğunu sayıp bunun derlemeyle koşum arasındaki
farkı gösterdi. Bu kurs, M08/K06 Kurumsal Uygulama Çerçevesi, altıncı ve son soruyu sordu:
bir davranışı kaynakta ne doğurdu — bir çağrı mı, bir açıklama mı, yoksa hiçbiri mi. Altı
soru da aynı kaynak metne bakıyor, ama her biri farklı bir boşluğu arıyor; biri diğerinin
genellemesi değil ve hiçbiri kaynağı yukarıdan aşağı okumanın yeterli olduğunu söylemiyor.

M08 burada kapanıyor. Kaynak metin, bir Java programının ne yaptığının yalnız bir kısmını
anlatıyor — eklenen adımı, kararı veren tipi, garantiyi veren tarafı, koşturan iş
parçacığını, gerekli sınıf yolunu, davranışın kaynağını; altısı da kaynağı okuyanın gözünden
en az kısmen saklı ve altısı da yalnız koşturularak, sayılarak görünür hâle geliyor.

