İçeriğe geç
academia.sh

Ders 05 / 19

Otomatik Yapılandırma

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.

İçindekiler

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

// 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 1OtomatikBildirimci 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 payın 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 payın 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.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat