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 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ı
cozyö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’ten2’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.