İçeriğe geç
academia.sh

Ders 05 / 10

Paylaşılan Mantık, Yerel Arayüz

Paylaşılan mantık ve yerel arayüz yaklaşımı iş kurallarını ortak bir modülde tutar, arayüzü her platformda ayrı yazar. Ders form doğrulamasının ortak, formu kuran arayüz işlevlerinin ikiz çıktığını ölçer ve ayrışmış ikizin bakım bedelini gösterir.

Yayın Güncelleme

Bu derste

  • Paylaşılan mantık ve yerel arayüz yaklaşımının hangi katmanı ortak, hangisini ayrı yazdığını açıklar
  • Form doğrulamasının iki platformda aynı işlevle çalıştığı için ortak çıktığını ölçer
  • Formu kuran arayüz işlevlerinin ayrı yazıldığı için sonuçları aynı olsa bile ikiz çıktığını gösterir
  • İki arayüz işlevinden biri bir alanı unutunca paylaşımın ikiz kaldığını, bedelin düzyazıda söylendiğini açıklar
İçindekiler

Önceki ders her şeyi çizen bir motoru ölçtü ve paylaşımı en geniş hâlinde buldu. Bu ders ters yöne gider. Uygulamanın iş kuralları, yani doğrulama, hesap ve iletişim kuralları, platformdan bağımsız bir modülde bir kez yazılır. Arayüz ise her platformda kendi araçlarıyla ayrı yazılır. Ortak modül iki arayüzün altında durur; arayüz ona çağrı yapar, ama arayüzün kendisi paylaşılmaz.

Kursun sorusu değişmez: bu özelliğin kodu platformlar arasında nasıl paylaşılıyor? Bu derste soru iki katmana ayrı ayrı sorulur. Mantık katmanı için yanıt tek koddur. Arayüz katmanı için yanıt iki koddur.

Düzenek şöyle çalışır. Ortak modül bir form girdisini alır ve doğrulama sonucunu döndürür. Her platformun arayüzü kendi bileşenleriyle formu kurar, kullanıcının yazdığını ortak modüle verir ve dönen sonucu kendi biçiminde gösterir. Arayüz platformun kurallarına tam uyar: gezinme, erişilebilirlik ve klavye davranışı platformun kendisindendir. Bedel, arayüzün iki kez yazılmasıdır.

Kurs bu bedeli sınıflar, ama başka bir ölçüyle ölçer. Bir sorunun cevabını bilmek gerekir: bu yapının bedeli nereye düşüyor? (M11/K06). O kurs, bedelin hangi aşamada ödendiğini sorar. Bu kurs bedelin yerini değil, kodun kaç kez yazıldığını sınıflar; ikizin bakım bedeli aşağıda düzyazıda söylenir.

YA1. Model, bu paylaşımın kuralını kurar; model bir arayüzü çizmez, bir formu ekrana koymaz ve kullanıcıdan girdi almaz. Modelde form bir nesnedir: name alanı bir ad, phone alanı bir telefon dizgisidir. Doğrulama kuralı da modelde seçilmiş bir kuraldır: ad boş olamaz, telefon en az beş karakter taşır. Arayüz işlevleri formu çizmez; hangi alanların ve düğmelerin kurulacağını bir dizgi olarak döndürür.

// sharing.mjs — bir ozelligin kodunun platformlar arasinda nasil paylasildigini siniflandirir
export const PLATFORMS = ["ios", "android"];

export class Sharing {
  constructor(label, impl, samples) {
    this.label = label;
    this.total = samples.length;
    this.missing = PLATFORMS.filter((p) => typeof impl[p] !== "function");
    const present = PLATFORMS.filter((p) => !this.missing.includes(p));
    this.code = new Set(present.map((p) => impl[p])).size;
    if (this.missing.length > 0) {
      this.differ = null;
      this.verdict = "tek";
      return;
    }
    this.differ = samples.filter((s) => {
      const outs = PLATFORMS.map((p) => JSON.stringify(impl[p](s, p)));
      return new Set(outs).size > 1;
    }).length;
    if (this.code > 1) this.verdict = "ikiz";
    else this.verdict = this.differ === 0 ? "ortak" : "dalli";
  }

  line() {
    const detail = this.differ === null
      ? `eksik=${this.missing.join(",")}`
      : `fark=${this.differ}/${this.total}`;
    console.log(`${this.label}: kod=${this.code} | ${detail} -> ${this.verdict}`);
  }
}

export function countSharing(items) {
  for (const name of ["ortak", "dalli", "ikiz", "tek"]) {
    console.log(`${name}: ${items.filter((s) => s.verdict === name).length}`);
  }
}

Doğrulama kuralını ve hata metinlerini ortak bir dosyaya koyuyoruz. Sonraki üç ölçüm onu içe alır.

// form.mjs — ortak modul: dogrulama kurali ve hata metinleri
export function validateForm(form) {
  if (form.name === "") return "ad-eksik";
  if (form.phone.length < 5) return "telefon-kisa";
  return "gecerli";
}

const MESSAGES = {
  "ad-eksik": "Ad bos birakilamaz",
  "telefon-kisa": "Telefon cok kisa",
  "gecerli": "",
};

export function errorText(code) {
  return MESSAGES[code];
}

Bu dosya iki platformun arayüzünün de altında durur. Hiçbir satırı platformu okumaz ve hiçbir satırı bir bileşen oluşturmaz.

Mantık Ortak, Arayüz İkiz

İlk ölçümde iki özelliği yan yana koyuyoruz. Birincisi doğrulamadır: iki platform da aynı validateForm işlevini çağırır. İkincisi formu kuran arayüz işlevidir: iOS için iosForm, Android için androidForm. İkisi ayrı yazılmıştır ve doğrulama sonucuna göre bir hata alanı eklerler.

// main.mjs
import { Sharing, countSharing } from "./sharing.mjs";
import { validateForm } from "./form.mjs";

function iosForm(form) {
  const parts = ["ad", "telefon", "gonder"];
  if (validateForm(form) !== "gecerli") parts.push("hata");
  return parts.join(",");
}

function androidForm(form) {
  const fields = ["ad", "telefon"];
  const base = fields.concat(["gonder"]);
  return validateForm(form) === "gecerli" ? base.join(",") : base.concat("hata").join(",");
}

const forms = [
  { name: "Ada", phone: "55501" },
  { name: "", phone: "55501" },
  { name: "Ada", phone: "12" },
  { name: "Can", phone: "55502" },
];

const features = [
  new Sharing("form-dogrulama", { ios: validateForm, android: validateForm }, forms),
  new Sharing("form-arayuzu", { ios: iosForm, android: androidForm }, forms),
];
features.forEach((f) => f.line());
countSharing(features);
form-dogrulama: kod=1 | fark=0/4 -> ortak
form-arayuzu: kod=2 | fark=0/4 -> ikiz
ortak: 1
dalli: 0
ikiz: 1
tek: 0

İki satır aynı biçimde okunur. form-dogrulama için kod=1 ve fark=0/4 çıktı; bu ortak bir özelliktir. form-arayuzu için fark=0/4 çıktı, yani iki işlev dört formun hepsinde aynı alanları üretti, ama kod=2 olduğu için paylaşım ikizdir. Kural sonucu değil kodun sayısını görür. Aynı çıktıyı veren iki ayrı kod, tek bir kod değildir.

Bu fark kursun en çok karıştırılan yeridir. Sonuçların aynı olması iyi bir haberdir, ama bir güvence değildir. İki işlev bugün aynı sonucu veriyor, çünkü aynı kişi aynı gün ikisini de yazdı. Bir gün biri değişir ve öteki değişmezse sonuçlar ayrışır. İkizin tanımı bu ihtimale dayanır: iki kod vardır ve her biri ayrı ayrı bakım ister.

Aynı ölçümün kaynak karşılıkları şöyledir. İki parça ölçülmez; model arayüzü çalıştırmaz. Yine de iki ayrı kod tabanının neye benzediğini gösterirler.

// Platform kodu: SignupForm
import SwiftUI

struct SignupForm: View {
    @State private var name = ""
    @State private var phone = ""
    let validate: (String, String) -> String

    var body: some View {
        Form {
            TextField("Ad", text: $name)
            TextField("Telefon", text: $phone)
            Button("Gonder") { _ = validate(name, phone) }
        }
    }
}
// Platform kodu: SignupForm
@Composable
fun SignupForm(validate: (String, String) -> String) {
    var name by remember { mutableStateOf("") }
    var phone by remember { mutableStateOf("") }
    Column {
        TextField(value = name, onValueChange = { name = it })
        TextField(value = phone, onValueChange = { phone = it })
        Button(onClick = { validate(name, phone) }) { Text("Gonder") }
    }
}

İki parçanın ortak yanı validate parametresidir: ikisi de doğrulamayı dışarıdan alır. Ortak modülün işi budur. Ayrı olan her şey ise parçaların gövdesindedir: durum tutma, alan bileşeni ve düğme platformun kendi araçlarıdır.

Hata Görünümü: Ortak Metin, İkiz Çizim

Hata metni de iki katmana ayrılır. Metnin kendisi, yani “Telefon cok kisa” cümlesi, bir kural olarak ortak modülde durur: bir hata kodu girer, bir metin çıkar. Metnin ekranda nasıl göründüğü ise platformun işidir. Her platform hata satırını kendi görsel diliyle çizer.

YA2. Modelde “görünüm” bir dizgidir: iOS arayüz işlevi metni köşeli ayraçla, Android arayüz işlevi metni açılı ayraçla sarar. Ayraç seçimi modelin keyfi bir temsilidir; gerçek platformların hata görünümleriyle ilgisi yoktur.

// main.mjs
import { Sharing, countSharing } from "./sharing.mjs";
import { errorText } from "./form.mjs";

const iosBanner = (code) => `[${errorText(code)}]`;
const androidBanner = (code) => `<${errorText(code)}>`;

const codes = ["ad-eksik", "telefon-kisa", "gecerli"];

const features = [
  new Sharing("hata-metni", { ios: errorText, android: errorText }, codes),
  new Sharing("hata-gorunumu", { ios: iosBanner, android: androidBanner }, codes),
];
features.forEach((f) => f.line());
countSharing(features);
hata-metni: kod=1 | fark=0/3 -> ortak
hata-gorunumu: kod=2 | fark=3/3 -> ikiz
ortak: 1
dalli: 0
ikiz: 1
tek: 0

Bu ölçümde ikiz ile dallı arasındaki çizgi görünür. hata-gorunumu için fark=3/3 çıktı: iki platform üç kodun üçünde de farklı sonuç verdi. Yine de paylaşım dallı değil ikizdir, çünkü iki işlev ayrı yazılmıştır. Dallı, tek bir işlevin platforma göre dallanmasıdır. İkiz, iki işlevin var olmasıdır. Sonuçların farklı olması burada kasıtlıdır: her platform kendi görünümünü ister ve iki arayüz işlevini bu yüzden ayırmak, yaklaşımın seçilmiş bir bedelidir.

Bir tasarım kararı da buradan çıkar. Hata metni ortak modülde durduğu için metni değiştirmek tek bir yerde yapılır ve iki platforma birlikte gider. Görünümü değiştirmek iki yerde yapılır. Yaklaşım, değişme sıklığı farklı olan şeyleri farklı katmanlara koyar: sık değişen ve platformdan bağımsız olan kural ortak modüle, platforma bağlı olan çizim yerel arayüze.

Sınırlayıcı Ölçüm

İkizin bedeli, iki kodun ayrışabilmesidir. Bunu ölçmek için aynı arayüz işlevinin iki sürümünü karşılaştırıyoruz. Eşleşmiş çift her formda aynı alanları üretir. Ayrışmış çiftte Android sürümü yeni eklenen telefon alanını unutmuştur.

YA3. Modelde “unutmak” bir yazım hatasıdır: androidFormStale işlevi telefon alanını listeye hiç koymaz. Gerçek ekiplerde bu, bir platformda yapılan değişikliğin öteki platforma taşınmaması olarak olur; model bu süreci taklit etmez, yalnız sonucunu bir işlev olarak yazar.

// main.mjs
import { Sharing, countSharing } from "./sharing.mjs";
import { validateForm } from "./form.mjs";

function iosForm(form) {
  const parts = ["ad", "telefon", "gonder"];
  if (validateForm(form) !== "gecerli") parts.push("hata");
  return parts.join(",");
}

function androidForm(form) {
  const base = ["ad", "telefon", "gonder"];
  return validateForm(form) === "gecerli" ? base.join(",") : base.concat("hata").join(",");
}

function androidFormStale(form) {
  const base = ["ad", "gonder"];
  return validateForm(form) === "gecerli" ? base.join(",") : base.concat("hata").join(",");
}

const forms = [
  { name: "Ada", phone: "55501" },
  { name: "", phone: "55501" },
  { name: "Ada", phone: "12" },
  { name: "Can", phone: "55502" },
];

const features = [
  new Sharing("form-eslesik", { ios: iosForm, android: androidForm }, forms),
  new Sharing("form-ayrismis", { ios: iosForm, android: androidFormStale }, forms),
];
features.forEach((f) => f.line());
countSharing(features);
form-eslesik: kod=2 | fark=0/4 -> ikiz
form-ayrismis: kod=2 | fark=4/4 -> ikiz
ortak: 0
dalli: 0
ikiz: 2
tek: 0

İki özellik de ikiz çıktı. form-eslesik için fark=0/4, form-ayrismis için fark=4/4 okunur ve ikisinin paylaşımı aynıdır. Kural fark sayısını raporlar ama paylaşımı belirlemede kullanmaz: kod=2 olduğu sürece ikizdir. Bu, kuralın sınırıdır ve bilerek böyle kurulmuştur. Sıfır fark bugünün durumudur. Sıfırdan büyük fark ise ikizin ayrışmış hâlidir ve ölçüm bunu yakalar; yine de paylaşımı değiştirmez.

Ayrışmış ikizin bedeli düzyazıda söylenir, çünkü kural onu sayamaz. Android sürümü telefon alanını unuttuğunda doğrulama hâlâ telefonu kontrol eder, ama kullanıcı alanı göremez. Hata, doğrulama katmanında değil arayüz katmanındadır ve kimse doğrulama sınamalarında bulamaz. Bu tür kusurlar iki platformun ekranı yan yana konup karşılaştırılana kadar saklanır. Ekip büyüdükçe karşılaştırma daha seyrek yapılır ve kusur daha uzun yaşar.

Bu yüzden yaklaşımın ikizini ölçmek yetmez; ikizin ayrışmaması için bir güvence gerekir. Ortak bir arayüz belirtimi, iki platformun ekran görüntülerini karşılaştıran bir sınama ya da her alan eklemesinin iki platformu birlikte gerektirdiği bir inceleme kuralı bu güvencelerdendir. Kural bunların hiçbirini görmez; yalnız kodun iki kez yazıldığını görür.

Özet

  • Bu dersin paylaşımı: ikiz. Mantık katmanı ortak modülde tek koddur, ama dersin taşıyıcı ölçümü olan arayüz iki platformda ayrı yazılmıştır.
  • Form doğrulaması iki platformda aynı işlevle çalıştı ve dört formda fark vermedi: paylaşım ortak çıktı.
  • Formu kuran arayüz işlevleri dört formun hepsinde aynı alanları üretti ama iki ayrı kod olduğu için paylaşım ikiz çıktı: sonucun aynı olması tek kod demek değildir.
  • İki arayüz işlevinden biri bir alanı unutunca fark sıfırdan büyük oldu, ama paylaşım yine ikiz kaldı; ayrışmış ikizin bakım bedeli kuralın değil düzyazının konusudur.
  • Yaklaşım sık değişen platformdan bağımsız kuralı ortak modüle, platforma bağlı çizimi yerel arayüze koyar; bedeli arayüzün iki kez yazılması ve iki yazımın birbirinden uzaklaşma ihtimalidir.

Sonraki Adım

Bu derste ve önceki derslerde beş yaklaşım tek tek ölçüldü. Her yaklaşımın bir paylaşımı oldu: ortak, dallı ve ikiz çıktılar gördük. Sıradaki ders, Yaklaşım Seçim Ölçütleri, aynı özellik listesini iki yaklaşımla yeniden ölçer ve kursun dört paylaşımının tek bir ekranda nasıl dağıldığını gösterir.

İ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