İçeriğe geç
academia.sh

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.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat