Ders 09 / 24
Akış Diyagramları
Akışın çizge olarak kurulması; erişilemeyen durumun ve çıkışsız düğümün hesapla aranması, çevrimin kusur olup olmadığının ayrılması ve yol sayısının sınama yükünü belirlemesi.
İçindekiler
Gezinme yapısı ögelere nasıl ulaşıldığını anlatıyor ama bir görevin içindeki kararları anlatmıyor. Ödünç alma tek bir tıklama değildir: oturum açık mı diye bakılır, kaydın durumu denetlenir, kayıt ödünçteyse rezervasyon önerilir, üyenin gecikmiş kitabı varsa işlem durur. Bu dallanmalar düz yazıyla anlatıldığında hangi durumun hangi duruma bağlandığı kaybolur ve eksik dallar fark edilmez.
Akış diyagramı (flow diagram) bu yapıyı açık hâle getirir: her durum bir düğüm, her geçiş bir kenardır. Diyagramın değeri çizim olmasında değil, denetlenebilir olmasındadır. Veri Yapıları kursunda tanıtılan çizge kavramı burada doğrudan işe yarar: bir akış çizge olarak yazıldığı anda, üzerinde çalışan sorular hesapla yanıtlanır.
Akışın Düğümleri ve Geçişleri
Üç düğüm türü vardır ve ayrılmaları gerekir.
- Durum düğümü. Kullanıcının bulunduğu bir yer: giriş ekranı, kayıt ayrıntısı, ödünç onayı. Kullanıcı burada bir şey görür.
- Karar düğümü. Sistemin ya da kullanıcının bir dallanma ürettiği nokta: oturum var mı, gecikmiş kitap var mı. Karar düğümünün çıkışları birbirini dışlamalı ve bütün durumları kapsamalıdır; iki dallı bir karardan yalnızca bir dal çıkıyorsa diyagram eksiktir.
- Bitiş düğümü. Akışın tamamlandığı yer. Bir akışta birden çok bitiş olabilir ve hepsi başarı olmak zorunda değildir; vazgeçme de bir bitiştir ve yazılır.
Yazılmayan bitiş, tasarlanmamış bitiştir. Kullanıcı yine de vazgeçer, ama nereye düştüğü belirsizdir.
İki Kusurun Aranması
Çizge yazıldıktan sonra iki kusur hesapla aranır. Erişilemeyen durum, başlangıçtan hiçbir geçiş dizisiyle ulaşılamayan düğümdür; tasarlanmış ama akışa bağlanmamış bir ekrandır. Çıkışsız düğüm, hiçbir bitişe ulaşamayan düğümdür; kullanıcının içine düşüp çıkamayacağı bir yerdir.
// akis.mjs — akis cizgesinde erisilemeyen durum, cikissiz dugum ve yol sayimi // Odunc alma akisinin dugumleri ve gecisleri (ders icin kurulmus veri) const BASLANGIC = "basla"; const BITISLER = ["odunc basarili", "rezervasyon basarili", "vazgec"]; const GECIS = { "basla": ["oturum var mi"], "oturum var mi": ["kayit durumu", "giris ekrani"], "giris ekrani": ["kayit durumu", "giris hatasi"], "giris hatasi": ["giris ekrani", "vazgec"], "kayit durumu": ["rafta", "oduncte"], "rafta": ["gecikmis kitap var mi"], "gecikmis kitap var mi": ["borc odeme", "odunc onayi"], "borc odeme": ["odunc onayi"], "odunc onayi": ["odunc basarili", "sistem hatasi"], "oduncte": ["rezervasyon onerisi"], "rezervasyon onerisi": ["rezervasyon onayi", "vazgec"], "rezervasyon onayi": ["rezervasyon basarili"], "uye degil bilgisi": ["vazgec"], "sistem hatasi": [], "odunc basarili": [], "rezervasyon basarili": [], "vazgec": [], }; const DUGUMLER = Object.keys(GECIS); // 1. Baslangictan erisilebilen dugumler const gez = (baslangic, komsu) => { const g = new Set([baslangic]), yigit = [baslangic]; while (yigit.length) for (const k of komsu(yigit.pop())) if (!g.has(k)) { g.add(k); yigit.push(k); } return g; }; const erisilen = gez(BASLANGIC, (d) => GECIS[d]); // 2. Bir bitise ulasabilen dugumler (ters cizgede gezinme) const TERS = {}; for (const d of DUGUMLER) TERS[d] = []; for (const [d, komsular] of Object.entries(GECIS)) for (const k of komsular) TERS[k].push(d); const ulasan = new Set(); for (const b of BITISLER) for (const d of gez(b, (x) => TERS[x])) ulasan.add(d); console.log("dugum giris cikis baslangictan bitise"); for (const d of DUGUMLER) { console.log( `${d.padEnd(24)} ${String(TERS[d].length).padStart(5)} ${String(GECIS[d].length).padStart(6)} ` + `${(erisilen.has(d) ? "erisilir" : "ERISILMEZ").padStart(13)} ${(ulasan.has(d) ? "ulasir" : "ULASMAZ").padStart(7)}` ); } const erisilemez = DUGUMLER.filter((d) => !erisilen.has(d)); const cikissiz = DUGUMLER.filter((d) => !ulasan.has(d)); console.log(`\nerisilemeyen dugum : ${erisilemez.join(", ") || "yok"}`); console.log(`cikissiz dugum : ${cikissiz.join(", ") || "yok"}`); // 3. Cevrim var mi (donen yol) const RENK = {}; const cevrimler = []; (function ara(d, yol) { RENK[d] = 1; for (const k of GECIS[d]) { if (RENK[k] === 1) cevrimler.push([...yol.slice(yol.indexOf(k)), k].join(" -> ")); else if (!RENK[k]) ara(k, [...yol, k]); } RENK[d] = 2; })(BASLANGIC, [BASLANGIC]); console.log(`cevrim : ${cevrimler.join(" | ") || "yok"}`); // 4. Baslangictan bitislere giden yinelemesiz yollar const yollar = []; (function yuru(d, yol) { if (BITISLER.includes(d)) { yollar.push(yol); return; } for (const k of GECIS[d]) if (!yol.includes(k)) yuru(k, [...yol, k]); })(BASLANGIC, [BASLANGIC]); console.log(`\nyinelemesiz yol sayisi: ${yollar.length}`); const uzunluklar = yollar.map((y) => y.length - 1); console.log(`en kisa yol: ${Math.min(...uzunluklar)} gecis, en uzun yol: ${Math.max(...uzunluklar)} gecis`); console.log("\nbitis yol sayisi en kisa"); for (const b of BITISLER) { const bunlar = yollar.filter((y) => y.at(-1) === b); console.log( `${b.padEnd(21)} ${String(bunlar.length).padStart(10)} ${bunlar.length ? Math.min(...bunlar.map((y) => y.length - 1)) : "-"}` ); } console.log("\nen kisa basarili yol:"); console.log(" " + yollar.filter((y) => y.at(-1) === "odunc basarili").sort((a, b) => a.length - b.length)[0].join(" -> "));
dugum giris cikis baslangictan bitise basla 0 1 erisilir ulasir oturum var mi 1 2 erisilir ulasir giris ekrani 2 2 erisilir ulasir giris hatasi 1 2 erisilir ulasir kayit durumu 2 2 erisilir ulasir rafta 1 1 erisilir ulasir gecikmis kitap var mi 1 2 erisilir ulasir borc odeme 1 1 erisilir ulasir odunc onayi 2 2 erisilir ulasir oduncte 1 1 erisilir ulasir rezervasyon onerisi 1 2 erisilir ulasir rezervasyon onayi 1 1 erisilir ulasir uye degil bilgisi 0 1 ERISILMEZ ulasir sistem hatasi 1 0 erisilir ULASMAZ odunc basarili 1 0 erisilir ulasir rezervasyon basarili 1 0 erisilir ulasir vazgec 3 0 erisilir ulasir erisilemeyen dugum : uye degil bilgisi cikissiz dugum : sistem hatasi cevrim : giris ekrani -> giris hatasi -> giris ekrani yinelemesiz yol sayisi: 9 en kisa yol: 4 gecis, en uzun yol: 8 gecis bitis yol sayisi en kisa odunc basarili 4 6 rezervasyon basarili 2 6 vazgec 3 4 en kisa basarili yol: basla -> oturum var mi -> kayit durumu -> rafta -> gecikmis kitap var mi -> odunc onayi -> odunc basarili
İki Kusurun Anlamı
Üye değil bilgisi düğümüne hiçbir geçiş girmiyor. Bu ekran tasarlanmış: üye olmayan bir ziyaretçinin ödünç alamayacağını anlatıyor ve vazgeçmeye bağlanıyor. Ama akışta bu duruma düşülmüyor. Giriş ekranından çıkan iki dal var — başarılı ve hatalı — ve “kişi zaten üye değil” durumu hiçbirinde değil. Kusur ekranda değil, karar düğümünün eksik dallanmasında. Erişilemeyen ekranlar çoğu zaman bir kararın eksik yazıldığının kanıtıdır.
Sistem hatası düğümünden hiçbir geçiş çıkmıyor. Kullanıcı ödünç onayı sırasında bir sistem hatasıyla karşılaşırsa bu düğüme düşüyor ve hiçbir bitişe ulaşamıyor. Arayüz Tasarımının Temelleri kursundaki Hata ve Uyarı Durumları dersinde konan kural burada çizge diliyle yeniden ifade edilir: her hata durumundan en az bir çıkış olmalıdır. Çıkış “yeniden dene” olabilir, “vazgeç” olabilir; olmaması bir seçenek değildir.
basla düğümünün giriş sayısının sıfır olması bir kusur değildir; başlangıç düğümü tanımı
gereği böyledir. Denetimin bunu ayırt etmesi için başlangıç ayrıca bildirilir.
Çevrim Bir Kusur Değildir
Denetim bir çevrim buldu: giriş ekranı ile giriş hatası arasında. Kullanıcı yanlış parola girdiğinde hata ekranına düşüyor, oradan giriş ekranına dönüyor ve yeniden deniyor.
Bu bir kusur değil, tasarlanmış bir yeniden denemedir. Çevrimin kusur olup olmadığını belirleyen tek ölçüt vardır: çevrimden çıkan bir kenar var mı? Giriş hatası düğümünün iki çıkışı var — giriş ekranı ve vazgeç — dolayısıyla kullanıcı döngüde kilitlenmez. Çıkışı olmayan bir çevrim, çıkışsız düğümün çok düğümlü hâlidir.
Çevrimin ikinci sorusu, kaç kez dönülebileceğidir. Sonsuz deneme parola güvenliği açısından sorunlu, sıfır deneme kullanılabilirlik açısından sorunludur; diyagramda bu, geçişin üzerine yazılan bir koşulla belirtilir ve koşul yazılmadıysa karar verilmemiş demektir.
Yol Sayısı Sınama Yükünü Belirler
Başlangıçtan bitişlere giden dokuz yinelemesiz yol var. Bu sayı doğrudan bir sınama bütçesidir: akışı tam sınamak, dokuz ayrı senaryo yürütmek demektir. En kısa yol dört geçiş (vazgeçmeye giden yol), en uzunu sekiz geçiş.
Başarılı ödünç almanın en kısa yolu altı geçiş ve dört farklı yolu var. Dört yolun kaynağı iki ikili karardır: oturum açık mı ve gecikmiş kitap var mı. İki ikili karar dört bileşim üretir; buna sistem hatası dalı eklenmediği için sayı dörtte kalıyor.
Yol sayısının hızla büyümesi, akış tasarımının en pratik ölçütünü verir: her yeni ikili karar, sınanacak yol sayısını yaklaşık iki katına çıkarır. Bir kararın gerçekten gerekli olup olmadığı bu maliyetle birlikte sorulur. “Gecikmiş kitap var mı” kararı gereklidir çünkü kurumun kuralıdır; “kullanıcı daha önce bu kaydı görüntülemiş mi” kararı bir kolaylık sağlayabilir ama sınama yükünü ikiye katlar.
Aynı hesap küçük bir sadeleştirme de öneriyor. Borç ödeme düğümünün tek çıkışı var ve doğrudan ödünç onayına gidiyor; yani borç ödendikten sonra başarısızlık dalı yazılmamış. Ödeme başarısız olabilecek bir işlemse diyagram eksik, olamayacak bir işlemse düğüm bir karar değil bir bilgilendirmedir ve akıştan çıkarılabilir. Tek çıkışlı düğümler her zaman bu soruyu davet eder.
Diyagramın Sınırı
Akış çizgesi durumları ve geçişleri gösterir, zamanı göstermez. Bir geçişin ne kadar sürdüğü, kullanıcının bir düğümde ne kadar beklediği ve bekleme sırasında ne gördüğü çizgede yoktur; bunlar yolculuk haritasının ve bileşen durumlarının işidir.
İkinci sınır, çizgenin kullanıcının zihnindeki akışı değil sistemin akışını göstermesidir. Kullanıcı “gecikmiş kitap var mı” diye bir karar vermez; borcunu ekranda görünce şaşırır. Diyagramın doğru olması, akışın anlaşılır olduğu anlamına gelmez. Anlaşılırlık ayrıca sınanır ve bu konunun son iki dersinin işidir.
Özet
- Akış diyagramı bir çizgedir: durum düğümleri, karar düğümleri ve bitiş düğümleri; karar düğümünün dalları birbirini dışlamalı ve bütün durumları kapsamalıdır.
- Erişilemeyen düğüm ileri gezinmeyle, çıkışsız düğüm ters çizgede bitişlerden gezinmeyle bulunur; örnek akışta biri eksik dallanmayı, öteki çıkışsız hata durumunu ortaya çıkardı.
- Çevrim kusur değildir; kusur, çevrimden çıkan kenarın bulunmaması ve dönüş sayısının koşula bağlanmamış olmasıdır.
- Yinelemesiz yol sayısı sınama yükünü verir; her yeni ikili karar yol sayısını yaklaşık iki katına çıkarır ve kararın gerekliliği bu maliyetle birlikte sorulur.
- Tek çıkışlı düğümler ya eksik bir başarısızlık dalını ya da akıştan çıkarılabilecek bir bilgilendirmeyi gösterir.
Sonraki Adım
Akış denetlendi ve gezinme yapısı kuruldu; ikisi de kâğıt üzerinde tutarlı. Tutarlı bir yapı kullanılabilir bir arayüz demek değildir ve bunu anlamanın tek yolu birine göstermektir. Ama neyi göstermeli — kutulardan oluşan kaba bir taslak mı, tıklanabilir bir kopya mı? Sonraki ders doğruluk düzeyi kararını ele alır: hangi sorunun hangi düzeyde yanıtlandığını, prototipin kaç düğümü kapsarsa kaç görevin uçtan uca sınanabileceğini ve bu iki oranın niçin eşit olmadığını hesaplar.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.