Ders 20 / 22
Hata ve Uyarı Durumları
Hatanın kaynağa ve kurtarılabilirliğe göre sınıflandırılması, doğrulama anının ürettiği ileti sayısının hesabı, önlenebilir hataların ayıklanması ve iletinin yakınlık kuralıyla yerleştirilmesi.
İçindekiler
Önceki ders boşluğun bir hata olmadığını gösterdi: sistem doğru çalışmış, sonuç gelmemişti. Sistemin doğru çalışmadığı ya da kullanıcının verdiği girdinin kabul edilemediği durumlar ise ayrı bir tasarım gerektirir.
Katalog arayüzünde üç ayrı örnek var: ISBN alanına geçersiz bir değer girildi, ödünç alma isteği sunucuya ulaşamadı, kullanıcının ödünç alma hakkı doldu. Üçü de “hata” diye adlandırılır ama üçünün kurtarma yolu farklıdır. Birincisinde kullanıcı girdiyi düzeltir, ikincisinde tekrar dener, üçüncüsünde yapabileceği bir şey yoktur. Aynı kutuda sunulamazlar.
Hata Kaynağa ve Kurtarılabilirliğe Göre Ayrılır
Sınıflandırma iki soruya dayanır: hata nereden geldi ve kullanıcı bunu düzeltebilir mi?
- Girdi hatası, kurtarılabilir. Kullanıcının verdiği değer kabul edilmedi. Kurtarma yolu değeri düzeltmektir. İleti alanın yanında durur ve neyin beklendiğini söyler.
- Durum hatası, kurtarılabilir. İşlem yapılamaz çünkü sistemin durumu uygun değil — kayıt başkasında, hak dolmuş. Kurtarma yolu farklı bir eylemdir: rezervasyon, iade tarihini bekleme. İleti eylemin yanında durur ve alternatifi gösterir.
- Geçici sistem hatası, kurtarılabilir. İstek ulaşmadı ya da zaman aşımına uğradı. Kurtarma yolu tekrar denemektir. İleti işlemin yanında durur ve tekrar deneme yolunu içerir.
- Kalıcı sistem hatası, kurtarılamaz. Kullanıcının yapabileceği bir şey yok. İleti durumu bildirir ve başka bir yol gösterir; “tekrar deneyin” yazmaz, çünkü tekrar deneme işe yaramayacaktır.
Ayrımın pratik değeri iletinin içeriğini belirlemesidir. Kurtarılabilir bir hatada ileti kurtarma yolunu taşımak zorundadır; taşımıyorsa kullanıcı için hata iletisi değil, yalnızca bir engel bildirimidir. Kurtarılamaz bir hatada ise kurtarma yolu önermek yanıltıcıdır.
Uyarı, hatadan bu eksende ayrılır: uyarıda işlem sürebilir. “Bu kayıt başka şubede” bir uyarıdır, çünkü kullanıcı yine de rezervasyon yapabilir. “Ödünç alma hakkınız doldu” bir hatadır, çünkü işlem yapılamaz. Renk seçimi bu ayrımı izler, tersi değil.
Doğrulama Anı İleti Sayısını Belirler
Girdi hatalarında en önemli karar, doğrulamanın ne zaman çalıştığıdır. Aynı doğrulama kuralı, farklı anlarda çalıştırıldığında farklı sayıda ileti üretir.
// dogrulama.mjs — dogrulama aninin uretilen hata iletisi sayisina etkisi // ISBN-13 saglama: ilk 12 basamak 1,3,1,3... agirlikli toplanir; kontrol basamagi tumler. function isbnGecerli(dizgi) { if (!/^\d{13}$/.test(dizgi)) return false; const b = [...dizgi].map(Number); let toplam = 0; for (let i = 0; i < 12; i++) toplam += b[i] * (i % 2 === 0 ? 1 : 3); return (10 - (toplam % 10)) % 10 === b[12]; } // Bir girdinin, tamamlanmadan once gecersizligi kesin mi? function erkenGecersiz(onek) { return /[^0-9]/.test(onek) || onek.length > 13; } const OTURUMLAR = [ { ad: "dogru yazim", hedef: "9789750718533" }, { ad: "son basamak yanlis", hedef: "9789750718534" }, { ad: "harf karisti", hedef: "97897507I8533" }, ]; for (const o of OTURUMLAR) { const onekler = Array.from({ length: o.hedef.length }, (_, i) => o.hedef.slice(0, i + 1)); // 1) Her tus vuruşunda dogrula const herTus = onekler.filter((p) => !isbnGecerli(p)).length; // 2) Yalnizca kesin gecersizlikte uyar, kalanini bekle const erken = onekler.filter((p) => erkenGecersiz(p)).length; // 3) Alandan cikista dogrula const cikista = isbnGecerli(o.hedef) ? 0 : 1; // 4) Gonderimde dogrula const gonderimde = cikista; console.log( `${o.ad.padEnd(20)} her tus: ${String(herTus).padStart(2)} kesin gecersizlikte: ${String(erken).padStart(2)} ` + `alandan cikista: ${cikista} gonderimde: ${gonderimde} sonuc: ${isbnGecerli(o.hedef) ? "gecerli" : "gecersiz"}` ); } // Kesin gecersizlik hangi karakterde saptanabilir? console.log("\noturum ilk kesin gecersiz onek uzunlugu toplam uzunluk"); for (const o of OTURUMLAR) { const onekler = Array.from({ length: o.hedef.length }, (_, i) => o.hedef.slice(0, i + 1)); const i = onekler.findIndex(erkenGecersiz); console.log( `${o.ad.padEnd(20)} ${(i === -1 ? "yok" : String(i + 1)).padStart(33)} ${String(o.hedef.length).padStart(15)}` ); } // Katalog kayitlarinin ISBN alanlari gercekten gecerli mi? console.log("\nkayit isbn gecerli"); for (const isbn of ["9789750718533", "9789944882101", "9786055829476"]) { console.log(`${isbn} ${isbnGecerli(isbn)}`); }
dogru yazim her tus: 12 kesin gecersizlikte: 0 alandan cikista: 0 gonderimde: 0 sonuc: gecerli son basamak yanlis her tus: 13 kesin gecersizlikte: 0 alandan cikista: 1 gonderimde: 1 sonuc: gecersiz harf karisti her tus: 13 kesin gecersizlikte: 5 alandan cikista: 1 gonderimde: 1 sonuc: gecersiz oturum ilk kesin gecersiz onek uzunlugu toplam uzunluk dogru yazim yok 13 son basamak yanlis yok 13 harf karisti 9 13 kayit isbn gecerli 9789750718533 true 9789944882101 true 9786055829476 true
Birinci satır kararı tek başına veriyor. Kullanıcı ISBN’i doğru yazdı ve her tuş vuruşunda doğrulayan bir arayüz ona on iki hata iletisi gösterdi. Hiçbiri yanlış değildi: her ara adım gerçekten geçersiz bir ISBN’di. Ama hiçbiri de doğru değildi, çünkü kullanıcı henüz yazmayı bitirmemişti.
Buradan çıkan kural şudur: bir girdi tamamlanmadan geçersizliği kesin değilse doğrulanmaz. Alandan çıkışta doğrulama, doğru yazımda sıfır, yanlış yazımda bir ileti üretiyor; istenen davranış budur.
Üçüncü satır kuralın istisnasını gösteriyor. Harf karışan oturumda geçersizlik dokuzuncu karakterde kesinleşiyor; ondan sonra yazılacak hiçbir karakter değeri geçerli yapamaz. Kesinleşmiş bir geçersizliği tamamlanmayı bekleyerek saklamak, kullanıcıyı dört karakter daha boşuna yazdırmak demektir. Kural bu yüzden iki parçalıdır: kesinleşmemiş geçersizlik beklenir, kesinleşmiş geçersizlik anında bildirilir.
Bu ayrım doğrulama kuralının biçimini de belirler. Bir alanın kuralı yalnızca “geçerli mi” sorusunu değil, “bu önek geçerli bir değere uzayabilir mi” sorusunu da yanıtlayabilmelidir.
En İyi Hata İletisi Gösterilmeyenidir
Bazı hatalar hata değildir; girdinin biçimsel gürültüsüdür ve arayüz onları sessizce düzeltebilir.
// kurtarma.mjs — onlenebilir hatalarin ayiklanmasi ve iletinin yerlestirilmesi function isbnGecerli(dizgi) { if (!/^\d{13}$/.test(dizgi)) return false; const b = [...dizgi].map(Number); let toplam = 0; for (let i = 0; i < 12; i++) toplam += b[i] * (i % 2 === 0 ? 1 : 3); return (10 - (toplam % 10)) % 10 === b[12]; } // Bicimsel gurultuyu temizle: bosluk, tire, uzun tire, ayrac noktalari const normalle = (s) => s.replace(/[\s\-‐-―.]/g, ""); const GIRDILER = [ "9789750718533", "978-975-07-1853-3", " 9789944882101 ", "978 605 5829 47 6", "9789750718534", "97897507I8533", ]; console.log("girdi ham gecerli normalize gecerli sinif"); let onlenebilir = 0, gercek = 0; for (const g of GIRDILER) { const n = normalle(g); const ham = isbnGecerli(g), son = isbnGecerli(n); let sinif; if (ham) sinif = "sorunsuz"; else if (son) { sinif = "ONLENEBILIR"; onlenebilir++; } else { sinif = "gercek hata"; gercek++; } console.log( `${JSON.stringify(g).padEnd(24)} ${String(ham).padStart(11)} ${n.padEnd(24)} ${String(son).padStart(7)} ${sinif}` ); } console.log(`\nonlenebilir hata: ${onlenebilir} / ${GIRDILER.length} gercek hata: ${gercek} / ${GIRDILER.length}`); // Iletinin yerlestirilmesi: yakinlik kurali (grup ici bosluk < gruplar arasi bosluk) const ETIKET_Y = 20, ALAN_Y = 44, ILETI_Y = 18; const GRUP_ICI = 4; // etiket-alan ve alan-ileti araligi const GRUPLAR_ARASI = 24; // iki alan grubu arasindaki aralik const alanlar = ["Baslik", "Yazar", "ISBN", "Raf kodu"]; let y = 0; const konum = {}; for (const a of alanlar) { konum[a] = { etiket: y, alan: y + ETIKET_Y + GRUP_ICI }; y = konum[a].alan + ALAN_Y + GRUPLAR_ARASI; } console.log("\nalan etiket y alan y alan alt kenari"); for (const a of alanlar) { console.log(`${a.padEnd(10)} ${String(konum[a].etiket).padStart(8)} ${String(konum[a].alan).padStart(7)} ${String(konum[a].alan + ALAN_Y).padStart(16)}`); } const hataliAlan = "ISBN"; const altKenar = konum[hataliAlan].alan + ALAN_Y; const sonrakiEtiket = konum["Raf kodu"].etiket; console.log(`\nileti alanin altina konursa:`); console.log(` iletiye uzaklik (kendi alanindan): ${GRUP_ICI} px`); console.log(` iletinin alt kenari: ${altKenar + GRUP_ICI + ILETI_Y} px`); console.log(` sonraki alanin etiketine uzaklik: ${sonrakiEtiket - (altKenar + GRUP_ICI + ILETI_Y)} px`); console.log(` yakinlik kurali (kendi alanina daha yakin): ${GRUP_ICI < sonrakiEtiket - (altKenar + GRUP_ICI + ILETI_Y)}`); console.log(`\nileti formun ustunde ozet olarak konursa:`); console.log(` ozetten hatali alana dikey uzaklik: ${konum[hataliAlan].alan} px`); console.log(` arada kalan alan sayisi: ${alanlar.indexOf(hataliAlan)}`); // Yakinlik kurali saglanmiyorsa: gruplar arasi aralik en az ne olmali? const KAT = 3; // ileti kendi alanina, sonraki gruba gore en az bu kat daha yakin olmali const gerekenAralik = GRUP_ICI + ILETI_Y + KAT * GRUP_ICI; console.log(`\nileti bloguyla birlikte alanin altinda kaplanan yer: ${GRUP_ICI + ILETI_Y} px`); console.log(`gruplar arasi aralik en az: ${gerekenAralik} px (simdiki: ${GRUPLAR_ARASI} px)`); console.log(`yeni aralikta sonraki etikete uzaklik: ${gerekenAralik - GRUP_ICI - ILETI_Y} px, kendi alanina ${GRUP_ICI} px, oran ${(gerekenAralik - GRUP_ICI - ILETI_Y) / GRUP_ICI}`); // Ileti gorununce sonraki alanlar kayiyor mu? Yer ayrilirsa kaymaz. const kayma = GRUP_ICI + ILETI_Y; console.log(`\nileti icin yer ayrilmazsa sonraki her alan ${kayma} px asagi kayar`); console.log(`dort alanli formda toplam kayan alan sayisi (ISBN hatali): ${alanlar.length - 1 - alanlar.indexOf("ISBN")}`); console.log(`yer ayrilirsa kayma: 0 px, bedeli: her alanin altinda surekli ${kayma} px bos yer`);
girdi ham gecerli normalize gecerli sinif "9789750718533" true 9789750718533 true sorunsuz "978-975-07-1853-3" false 9789750718533 true ONLENEBILIR " 9789944882101 " false 9789944882101 true ONLENEBILIR "978 605 5829 47 6" false 9786055829476 true ONLENEBILIR "9789750718534" false 9789750718534 false gercek hata "97897507I8533" false 97897507I8533 false gercek hata onlenebilir hata: 3 / 6 gercek hata: 2 / 6 alan etiket y alan y alan alt kenari Baslik 0 24 68 Yazar 92 116 160 ISBN 184 208 252 Raf kodu 276 300 344 ileti alanin altina konursa: iletiye uzaklik (kendi alanindan): 4 px iletinin alt kenari: 274 px sonraki alanin etiketine uzaklik: 2 px yakinlik kurali (kendi alanina daha yakin): false ileti formun ustunde ozet olarak konursa: ozetten hatali alana dikey uzaklik: 208 px arada kalan alan sayisi: 2 ileti bloguyla birlikte alanin altinda kaplanan yer: 22 px gruplar arasi aralik en az: 34 px (simdiki: 24 px) yeni aralikta sonraki etikete uzaklik: 12 px, kendi alanina 4 px, oran 3 ileti icin yer ayrilmazsa sonraki her alan 22 px asagi kayar dort alanli formda toplam kayan alan sayisi (ISBN hatali): 1 yer ayrilirsa kayma: 0 px, bedeli: her alanin altinda surekli 22 px bos yer
Altı girdinin üçü, yalnızca ayraç taşıdığı için geçersiz görünüyor. Ayraçlar temizlendiğinde üçü de geçerli. Bu üç girdi için hata iletisi göstermek, kullanıcıya çözebileceği bir sorun vermez; onun zaten doğru olan girdisini reddeder.
Kural şu biçimde yazılır: arayüzün kabul edebileceği bir girdi için hata gösterilmez. Ayraç, baştaki ve sondaki boşluk, büyük–küçük harf farkı bu sınıfa girer. Sessiz düzeltmenin sınırı ise anlamı değiştirmemektir; bir basamağı düzeltmek sessizce yapılamaz, çünkü hangi basamağın yanlış olduğu bilinmez.
İletinin Yeri Yakınlık Kuralıyla Denetlenir
İkinci tablo grubu iletinin nereye konacağını ölçüyor ve form düzeninde bir kusur buluyor.
İleti alanın 4 piksel altına konduğunda, iletinin alt kenarı 274 pikselde bitiyor ve bir sonraki alanın etiketi 276 pikselde başlıyor. Aradaki boşluk 2 piksel. Yakınlık ve Gruplama dersinde kurulan kural burada ihlal ediliyor: ileti, ait olduğu alandan 4, ait olmadığı alandan 2 piksel uzakta. Ekranda okunduğunda ileti bir sonraki alana ait görünür.
Düzeltme hesaplanabilir. İleti bloğu alanın altında 22 piksel yer kaplıyor; iletinin kendi alanına en az üç kat daha yakın olması istendiğinde gruplar arası aralık 34 piksele çıkmalı. Yeni aralıkta ileti kendi alanına 4, sonraki etikete 12 piksel uzaklıkta oluyor.
Form üstündeki özet, yakınlık kuralını hiç sağlamaz: özet ile hatalı alan arasında 208 piksel ve iki alan var. Bu, özetin yanlış olduğu anlamına gelmez; özet farklı bir iş yapar — kaç hata olduğunu bildirir ve her birine bir bağlantı verir. Ancak özet, alan yanındaki iletinin yerini tutmaz. İkisi birlikte kullanılır: özet sayfanın başında, ileti alanın yanında.
Son blok bir yan etkiyi gösteriyor. İleti göründüğünde altındaki alanlar 22 piksel aşağı kayar; bu, önceki dersteki yerleşim kayması sorununun küçük ölçekli hâlidir. Kullanıcı iletiyi okurken tıklamak istediği alan yer değiştirir. Çözüm, ileti için baştan yer ayırmaktır; bedeli her alanın altında sürekli duran 22 piksellik boşluktur. Bu bedel ödenir, çünkü kayan bir formda yanlış alana tıklamak yeni bir hata üretir.
Özet
- Hata kaynağına ve kurtarılabilirliğine göre sınıflandırılır; sınıf, iletinin içeriğini ve konumunu belirler.
- Kurtarılabilir hatada ileti kurtarma yolunu taşımak zorundadır; kurtarılamaz hatada kurtarma önermek yanıltıcıdır.
- Uyarı ile hata, işlemin sürüp süremediğine göre ayrılır; renk seçimi bu ayrımı izler.
- Tamamlanmamış bir girdi, geçersizliği kesinleşmedikçe doğrulanmaz; her tuş vuruşunda doğrulamak doğru yazımda bile onlarca ileti üretir. Geçersizlik kesinleştiği anda ise bildirilir; doğrulama kuralı “bu önek geçerli bir değere uzayabilir mi” sorusunu yanıtlayabilmelidir.
- Arayüzün kabul edebileceği bir girdi için hata gösterilmez; biçimsel gürültü sessizce temizlenir, anlam değiştiren düzeltme yapılmaz.
- İletinin konumu yakınlık kuralıyla denetlenir ve ileti için baştan yer ayrılır.
Sonraki Adım
Hata iletisinin yanında bir simge kullanmak yaygın bir karardır ve bu ders o kararı gerekçelendirmedi. İşlevsel Renkler dersinde renge eşlik edecek ikinci kanal olarak simge sayılmıştı, ancak simgenin kendisi ne zaman anlam taşır, ne zaman yalnızca yer kaplar sorusu açık kaldı. Sonraki ders simgeyi anlam taşıyan ve süsleyici olarak ikiye ayırır, ayrımın erişilebilirlik ağacındaki karşılığını gösterir ve simgenin tek başına metnin yerini tutup tutamayacağını tartışır.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.