---
title: 'Çapraz Platform Yaklaşımları'
source: 'https://academia.sh/tr/kurslar/capraz-platform-yaklasimlari'
language: tr
updated: '2026-10-21T05:02:49+00:00'
license: 'CC BY-SA 4.0'
lessons: 10
outcomes:
    - 'Çapraz platform yaklaşımlarını çalışma modeline göre sınıflandırır'
    - 'Kod paylaşımının sınırlarını ve maliyetini değerlendirir'
    - 'Proje için yaklaşım seçimini ölçütlere bağlar'
---

# Çapraz Platform Yaklaşımları

Tek kod tabanından iki platforma uygulama üretmenin her yolu, bir özelliğin kodunu platformlar arasında bir biçimde paylaştırır: web görünümü arayüzü tek bir sayfadan çizer, köprü tabanlı yaklaşım ortak kodu platform bileşenlerine bağlar, kendi çizim motoruyla çalışan yaklaşım her pikseli kendisi çizer, paylaşılan mantık yaklaşımı yalnız iş kurallarını ortaklaştırır. Kurs her yaklaşımı ve her platform gerçeğini tek bir soruyla ölçer — bu özelliğin kodu platformlar arasında nasıl paylaşılıyor? Yaklaşımların çalışma modeli, seçim ölçütleri, platform tasarım dilleri, yerel modül ihtiyacı, başarım beklentileri ve mağaza kısıtları aynı çerçeveden geçirilir.

## Yaklaşımlar

Çapraz platform geliştirmenin gerekçesi, web görünümü tabanlı, köprü tabanlı ve kendi çizim motoruyla çalışan yaklaşımlar, paylaşılan mantık ile yerel arayüz ayrımı ve yaklaşım seçim ölçütleri.

### Çapraz Platform Gerekçesi

Source: https://academia.sh/tr/kurslar/capraz-platform-yaklasimlari/capraz-platform-gerekcesi

Çapraz platform geliştirmenin gerekçesi, aynı özelliğin iki platformda kaç ayrı kodla yazıldığıdır. Ders bir özelliğin kodunun paylaşımını dört yanıtla sınıflar: ortak, dallı, ikiz, tek.

Önceki iki kurs dizisi bir uygulamayı iki kez baştan sona kurdu: biri bir platform için, öteki öteki platform için. İki dizi yan yana okununca aynı özelliklerin iki kez yazıldığı görülür. Fiyatı biçimleyen işlev, bir izni isteyen çağrı, geri düğmesine verilen yanıt her iki kod tabanında da var. Bu kursun konusu bu tekrarın nedeni, bedeli ve onu azaltmak için öne sürülen yollardır.

Kursun tek sorusu bu dersten başlar: **bu özelliğin kodu platformlar arasında nasıl paylaşılıyor?** Yanıtın dört biçimi var ve kurs boyunca adları değişmez. Özellik iki platformda aynı kodla çalışıyor ve kod her örnekte iki platformda aynı sonucu veriyorsa paylaşım **ortak**tır. Aynı kod çalışıyor ama kod en az bir örnekte platforma göre farklı sonuç veriyorsa paylaşım **dallı**dır. Her platformun kendi kodu varsa, sonuçlar aynı olsa bile, paylaşım **ikiz**dir. Özelliğin kodu platformlardan birinde hiç yoksa paylaşım **tek**tir. Dört yanıtın üst adı paylaşımdır; bir özelliğin paylaşımı denince bu dörtten biri anlaşılır.

Bu soru, M13/K04'te sorduğumuz "bu sonucu ne belirliyor" sorusuna benzer ama aynı değildir. Orada platform sonucu belirleyen bir ortamdı ve ölçülen şey sonucun kaynağıydı. Burada sonucun kaynağı sorulmaz: aynı özelliğin kaç ayrı kodla ve hangi platformlarda yazıldığı sorulur.

#### Paylaşımın Kuralı

Paylaşımı etiket değil kural verir. Aşağıdaki çekirdek, bir özelliğin iki platformdaki uygulamasını ve bir örnek listesini alır. Bir platformun uygulaması yoksa paylaşım tektir. İki uygulama ayrı işlev nesneleriyse ikizdir. Aynı işlevse ve hiçbir örnekte iki platformun sonucu ayrışmıyorsa ortak, ayrışıyorsa dallıdır.

```javascript
// 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}`);
  }
}
```

`PLATFORMS` platform listesidir; `Sharing` sınıfı bir özelliğin etiketini, uygulamalarını ve örneklerini alır. `code` özelliği kaç ayrı işlev nesnesi bulunduğunu, `differ` özelliği sonucun kaç örnekte platforma göre ayrıştığını tutar. `countSharing` dört paylaşımı sabit sırayla sayar ve sıfır olanı da basar. Kural bu işi yapan tek yerdir: paylaşım hiçbir yerde elle atanmaz.

Kural iki şeyi görmez. Birincisi bedeldir: üç satırlık bir ikiz ile üç yüz satırlık bir ikiz aynı sayılır. İkincisi verilmeyen örnektir: kural yalnız elindeki örneklerde görüneni görür. Bedeli ve örnek seçimini ders düzyazıyla söyler, kural söylemez.

Derslerdeki ölçüm şöyle kurulur. Bir özelliğin iki platformdaki uygulaması bir işlev tablosu olarak yazılır: `ios` anahtarının altında bir işlev, `android` anahtarının altında bir işlev. Tablo gerçek bir derleyiciyi ya da platformu çalıştırmaz.

**YA1.** Model, bu paylaşımın kuralını kurar; model çapraz platform çerçevesinin kendisi değildir. Platformda bir özelliğin kodu bir dosyada, bir derleme hedefinde ya da bir modülde durur. Modelde ise kodun kendisi bir işlevdir ve iki platformda "aynı kod" demek, tabloda aynı işlev nesnesini göstermek demektir.

#### Dört Özellik, Dört Paylaşım

Bir uygulamanın dört özelliğini ele alalım. Fiyatı biçimleyen işlev, başlığı kısaltan işlev, izin isteği ve geri düğmesi. İlkinde iki platform aynı işlevi çalıştırır ve işlev platforma bakmaz. İkincisinde aynı işlev çalışır ama kısaltma sınırı platforma göre değişir. Üçüncüsünde her platformun kendi izin işlevi var; ikisi aynı metni üretse de iki ayrı işlev yazılmıştır. Dördüncüsünde geri düğmesi yalnız Android'de vardır, iOS tarafında işlev yoktur. Her özellik dört örnekle ölçülür.

```javascript
// main.mjs
import { PLATFORMS, Sharing, countSharing } from "./sharing.mjs";

function formatPrice(cents) {
  return `${Math.floor(cents / 100)}.${String(cents % 100).padStart(2, "0")}`;
}

function shortTitle(title, platform) {
  const limit = platform === "ios" ? 12 : 16;
  return title.length > limit ? title.slice(0, limit) + "..." : title;
}

function iosPermission(kind) {
  return `izin:${kind}`;
}

function androidPermission(kind) {
  return `izin:${kind}`;
}

function handleBack(screen) {
  return screen === "ana" ? "cik" : "geri";
}

const prices = [0, 199, 1250, 99999];
const titles = ["Kahve", "Haftalik plan", "Market listesi", "Yillik abonelik yenileme"];
const kinds = ["bildirim", "kamera", "konum", "rehber"];
const screens = ["ana", "sepet", "odeme", "profil"];

const features = [
  new Sharing("fiyat-bicimi", { ios: formatPrice, android: formatPrice }, prices),
  new Sharing("baslik-kisaltma", { ios: shortTitle, android: shortTitle }, titles),
  new Sharing("izin-istegi", { ios: iosPermission, android: androidPermission }, kinds),
  new Sharing("geri-tusu", { android: handleBack }, screens),
];
console.log(`platformlar: ${PLATFORMS.join(", ")}`);
features.forEach((f) => f.line());
countSharing(features);
```

```text
platformlar: ios, android
fiyat-bicimi: kod=1 | fark=0/4 -> ortak
baslik-kisaltma: kod=1 | fark=3/4 -> dalli
izin-istegi: kod=2 | fark=0/4 -> ikiz
geri-tusu: kod=1 | eksik=ios -> tek
ortak: 1
dalli: 1
ikiz: 1
tek: 1
```

Dört özellik dört ayrı paylaşım verdi: ortak 1, dallı 1, ikiz 1, tek 1. Bu dağılım kursun bağlayıcı sayısıdır; örnek sayısı 4, platform sayısı 2'dir. Sonraki dersler bu sayıyı yeniden basmaz, ona atıf yapar.

`fiyat-bicimi` ortaktır. `kod=1` iki platformun aynı işlevi çalıştırdığını, `fark=0/4` dört fiyatın ikisinde de aynı yazıldığını söyler. `baslik-kisaltma` dallıdır: kod yine tektir ama `shortTitle` sınırı platforma göre seçer. "Haftalik plan" on üç karakterdir: iOS'ta on ikinci karakterden sonra kısalır, Android'de kısalmaz. "Market listesi" on dört karakterdir ve aynı biçimde yalnız iOS'ta kısalır. "Yillik abonelik yenileme" yirmi dört karakterdir; iki platformda da kısalır ama farklı yerden. Dört başlığın üçünde sonuç ayrışır. `izin-istegi` ikizdir: `kod=2`, iki ayrı işlev demektir. `fark=0/4` ise ikisinin şimdilik aynı şeyi söylediğini gösterir. `geri-tusu` tektir: `eksik=ios` iOS uygulamasının bulunmadığını söyler ve `fark` hesaplanamaz.

#### Gerekçe Nereden Gelir

Çapraz platform kararı çoğu zaman üç gözlemden doğar. Birincisi ekip büyüklüğüdür: iki ayrı kod tabanı, iki ayrı beceri kümesi ve çoğu zaman iki ayrı ekip demektir. İkincisi ürün tutarlılığıdır: aynı kuralın iki platformda ayrı yazılması, kuralın iki platformda farklı sonuç vermesi riskini taşır. Üçüncüsü yayın ritmidir: bir özellik bir platformda çıkıp öteki platformda haftalarca bekleyebilir. Üçü de gerçek gerekçelerdir ve üçü de ölçüdeki aynı yere işaret eder: ikiz ve tek sayısını azaltmak.

Gerekçenin karşı yanı da aynı ölçüden okunur. Dallı bir özellik, kod tek olsa bile iki platformun farkını içinde taşır; o farkı yazan kişinin iki platformu da tanıması gerekir. Tek özellik ise kod tabanı birleşse bile ortaklaşmaz, çünkü ortak kodun çalışacağı yetenek bir platformda bulunmayabilir. Çapraz platform kararı, bu yüzden "iki kodu bire indirmek" diye değil, her özellik için "hangi paylaşıma düşer" sorusuyla verilir. Kursun sonraki dersleri bu soruyu dört yaklaşımın her biri için sorar ve son derste yaklaşım seçimine bağlar.

#### İki Kod Tabanının Bedeli

İkiz paylaşımın tipik görüntüsü şudur: aynı kural iki dilde iki kez yazılır. Aşağıdaki iki parça fiyat biçimleyicinin ikiz biçimidir. Sonuç aynıdır, kod iki kez yazılmıştır.

```swift
// Platform kodu: PriceFormatter
import Foundation

func formatPrice(cents: Int) -> String {
    String(format: "%d.%02d", cents / 100, cents % 100)
}
```

```kotlin
// Platform kodu: PriceFormatter
fun formatPrice(cents: Int): String =
    "%d.%02d".format(cents / 100, cents % 100)
```

Bu iki parça ölçülmez; model, bu paylaşımın kuralını kurar ve platform derleyicisini çalıştırmaz. Yine de bedeli göstermek için yeterlidir. Kuralı değiştiren biri iki dosyaya dokunur, iki dosyayı iki ayrı sınama düzeneğiyle sınar ve iki ayrı sürümde yayımlar. İki kod başlangıçta aynı sonucu verir; biri unutulduğunda ayrışır. Ayrışmış bir ikiz, ekip büyüdükçe bulunması zorlaşan bir kusurdur.

Tek kod tabanının vaadi bu bedeli bir kez ödemektir. Kural bir yerde yazılır, bir yerde sınanır ve iki platforma birlikte gider. Vaadin sınırı da ölçüde görünür: kod tek kalsa bile `baslik-kisaltma` gibi bir özellik platforma göre dallanır ve dal da bakım ister. Paylaşımı artırmak, özelliğin her örnekte aynı davranmasını sağlamaz.

#### Aynı Sepet, İki Platform

Sepet toplamı ortak bir kurala iyi bir örnektir. Sepetteki her satır bir birim fiyat ile bir adet taşır, toplam kuruş cinsinden bulunur ve ekrana yazılırken biçimlenir. İşlev platformu hiç okumaz. Aynı bloğa girdisinden bağımsız bir sabit de koyuyoruz: uygulama adı. Sabit değer döndüren işlev meşrudur ama her zaman ortak verir, çünkü hiçbir girdi sonucu değiştiremez. Bu bir güvence değil, kuralın doğal sonucudur.

```javascript
// main.mjs
import { Sharing, countSharing } from "./sharing.mjs";

function cartTotal(lines) {
  return lines.reduce((sum, [unitCents, quantity]) => sum + unitCents * quantity, 0);
}

function shopLabel() {
  return "Market Defteri";
}

const carts = [
  [[199, 2], [1250, 1]],
  [[99, 3]],
  [[0, 5], [500, 1]],
  [[99999, 1], [1, 1]],
];

const features = [
  new Sharing("sepet-toplami", { ios: cartTotal, android: cartTotal }, carts),
  new Sharing("sabit-etiket", { ios: shopLabel, android: shopLabel }, carts),
];
features.forEach((f) => f.line());
countSharing(features);
```

```text
sepet-toplami: kod=1 | fark=0/4 -> ortak
sabit-etiket: kod=1 | fark=0/4 -> ortak
ortak: 2
dalli: 0
ikiz: 0
tek: 0
```

İki özellik de ortak çıktı, `fark` ikisinde de sıfır. `sepet-toplami` ortaktır çünkü işlev yalnız kendi girdisine bakar. `sabit-etiket` ortaktır çünkü sonucu hiçbir girdiye bağlı değildir. İkisinin ortak çıkması aynı nedenden değildir: ilkinde işlev girdiyi gerçekten işledi ve iki platformda aynı sonucu verdi, ikincisinde işlenecek bir şey yoktu. Bu fark kuralda görünmez, çünkü kural yalnız `fark` değerine bakar. Okuyucunun işi, bir özelliğin ortak çıkması karşısında "bu özellik ne yaptı" sorusunu sormaktır.

**YA2.** Sepet satırlarındaki birim fiyatlar kuruş cinsinden tam sayıdır; model ondalık ve para birimi kurallarını bilmez. Platformda bu kurallar yerel ayar ve biçimleyici sınıfları üzerinden gelir; modelde ise toplam yalnız tam sayı toplamıdır.

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

Şimdi aynı işlevi iki kez ölçelim. Bir uygulama simgesinin rozet metnini düşünelim: okunmamış sayı, platforma göre farklı bir üst sınırı aşarsa "99+" ya da "999+" biçimine döner. Rozet işlevi platform bilgisini okur ve bir dal taşır. Önce dala hiç dokunmayan üç örnekle ölçülür: sıfır, beş ve kırk iki okunmamış ileti. Sonra aynı işlev dört örnekle ölçülür: listeye yüz elli eklenir.

```javascript
// main.mjs
import { Sharing, countSharing } from "./sharing.mjs";

function badgeText(count, platform) {
  const limit = platform === "ios" ? 99 : 999;
  return count > limit ? `${limit}+` : String(count);
}

const quiet = [0, 5, 42];
const busy = [0, 5, 42, 150];

const features = [
  new Sharing("rozet-3-ornek", { ios: badgeText, android: badgeText }, quiet),
  new Sharing("rozet-4-ornek", { ios: badgeText, android: badgeText }, busy),
];
features.forEach((f) => f.line());
countSharing(features);
```

```text
rozet-3-ornek: kod=1 | fark=0/3 -> ortak
rozet-4-ornek: kod=1 | fark=1/4 -> dalli
ortak: 1
dalli: 1
ikiz: 0
tek: 0
```

İşlev değişmedi; `badgeText` her iki ölçümde de aynı işlevdir ve `kod=1` kalır. Yalnız örnek listesi değişti. İlk üç örnek dalın eşiğinin altında kaldığı için iki platform aynı metni verdi ve paylaşım ortak çıktı. Yüz elli örneği iOS eşiğini aşar, Android eşiğini aşmaz; iOS "99+" üretirken Android "150" üretir ve paylaşım dallıya döndü.

Bu sonucun iki yorumu var ve ikisi de doğrudur. Birincisi, kuralın yalnız verilen örneklerde görüneni gördüğüdür: dal bir örneğe dokunmadıkça ortak görünür. Bir özelliğin ortak olduğunu söylemeden önce dalı tetikleyecek örneğin listede bulunduğundan emin olmak gerekir. İkincisi, çapraz platform gerekçesinin sınırıdır: kodu tek tutmak, dalı ortadan kaldırmaz. Dal kodun içine taşınmıştır, kuralın dışına çıkmamıştır.

**YA3.** İki eşik, 99 ve 999, modelin girdisidir. Gerçek bir uygulamada rozet sınırını platform ve ürün kararı belirler; derste bu sayılar bir platform kuralı gibi sunulmaz.

#### Özet

- Bu dersin paylaşımı: ortak. Ders dört paylaşımı tanıtır ve sepet toplamı iki platformda aynı işlevle, platformu okumadan çalışır.
- Çekirdek ölçümde dört özellik dört paylaşım verdi: ortak 1, dallı 1, ikiz 1, tek 1; örnek sayısı 4, platform sayısı 2.
- İkiz, aynı kuralın iki kez yazılmasıdır; sonuçlar aynı olsa bile bakım iki dosyaya ve iki sınamaya dağılır.
- Sabit değer döndüren işlev her zaman ortak çıkar, çünkü hiçbir girdi sonucu değiştirmez.
- Aynı rozet işlevi üç örnekle ortak, dördüncü örnek eklenince dallı çıktı: kural yalnız verilen örneklerde görüneni görür ve bedeli görmez.

#### Sonraki Adım

Bu derste kod tabanı sayısı ve paylaşım kavramı kuruldu. Çapraz platform yaklaşımları bu kavramı farklı düzeneklerle değiştirir. Sıradaki ders, `Web Görünümü Tabanlı Yaklaşım`, arayüzü tek bir sayfadan çizen düzeneği ve sayfanın platform kabuğundan aldığı girdinin paylaşımı nasıl değiştirdiğini ölçer.

### Web Görünümü Tabanlı Yaklaşım

Source: https://academia.sh/tr/kurslar/capraz-platform-yaklasimlari/web-gorunumu-tabanli-yaklasim

Web görünümü tabanlı yaklaşım arayüzü tek bir sayfadan çizer ve sayfayı her platformda bir yerel kabuğun içine yerleştirir. Ders bu düzeneğin paylaşımını ölçer ve sayfanın kabuktan aldığı girdinin paylaşımı nasıl değiştirdiğini gösterir.

Önceki ders dört paylaşımı tanıttı ve bir özelliğin kodunun iki platformda nasıl durduğunu sınıflamayı öğretti. Bu ders ilk yaklaşımı o sınıflamadan geçirir: web görünümü tabanlı yaklaşım. Kursun sorusu değişmez — **bu özelliğin kodu platformlar arasında nasıl paylaşılıyor?** — ve ders bu soruyu bir düzeneğe sorar.

Web görünümü, bir yerel uygulamanın içine yerleştirilen ve bir web sayfasını gösteren görünümdür. Uygulamanın arayüzü bu sayfadır: liste, form, düğme sayfanın içinde çizilir. Sayfayı çevreleyen yerel parçaya kabuk denir. Kabuk uygulamanın açılışını, pencereyi, uygulama mağazasına giden paketi ve sayfayla konuşan kanalı taşır. Bu düzeneğe melez uygulama da denir. Sayfa bir kez yazılır ve iki platformda yüklenir; kabuk her platformda ayrı yazılır ama ince tutulur.

Bu düzenek paylaşımın büyük kısmını sayfaya yığar. Sayfadaki her işlev iki platformda aynı işlevdir, çünkü iki kabuk da aynı dosyayı yükler. Dersin ölçüsü bunun nerede bozulduğunu bulmaktır.

#### Sayfanın Kendi Kodu

Bir liste satırını çizen işlevle başlayalım. Satır bir başlık, bir sayı ve bir sıra taşır; işlev bunlardan sayfaya yazılacak bir satır üretir. Aynı sayfa bir de satıra dokunulduğunda kabuğa gönderilecek iletiyi kurar. İki işlev de yalnız girdilerine bakar, platformu okumaz.

**YA1.** Model, bu paylaşımın kuralını kurar; model web görünümünü ya da bir sayfayı çalıştırmaz. Modelde sayfa kodu iki platformda aynı işlev nesnesidir ve satır, bir başlık, bir sayı, bir sıra ve bir sabitlenmiş bayrağı olan düz bir nesnedir.

```javascript
// 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}`);
  }
}
```
Aşağıdaki ölçüm iki özelliği dört satırla sınar.

```javascript
// main.mjs
import { Sharing, countSharing } from "./sharing.mjs";

function renderRow(row) {
  return `<li>${row.title} (${row.count})</li>`;
}

function encodeMessage(row) {
  return JSON.stringify({ type: "open", id: row.index });
}

const rows = [
  { index: 0, title: "Kahve", count: 2, pinned: false },
  { index: 1, title: "Haftalik plan", count: 7, pinned: true },
  { index: 2, title: "Market listesi", count: 0, pinned: false },
  { index: 3, title: "Yillik abonelik", count: 12, pinned: false },
];

const features = [
  new Sharing("satir-cizimi", { ios: renderRow, android: renderRow }, rows),
  new Sharing("ileti-bicimi", { ios: encodeMessage, android: encodeMessage }, rows),
];
features.forEach((f) => f.line());
countSharing(features);
```

```text
satir-cizimi: kod=1 | fark=0/4 -> ortak
ileti-bicimi: kod=1 | fark=0/4 -> ortak
ortak: 2
dalli: 0
ikiz: 0
tek: 0
```

İki özellik de ortak çıktı. `kod=1` iki kabuğun aynı işlevi çalıştırdığını, `fark=0/4` dört satırın ikisinde de aynı çizildiğini söyler. Bu, yaklaşımın vaadinin en saf halidir: satırın biçimi bir yerde yazılır, bir yerde sınanır ve iki platforma birlikte gider. Bir hata düzeltmesi iki platformda aynı anda yürürlüğe girer.

Sayfa, kabuğa şöyle ulaşır. Aşağıdaki parça sayfanın üç işlevini gösterir; kabuk iletiyi alan ve güvenli alan payını sayfaya yazan yerel kodu taşır.

```html
<!-- Platform kodu: sayfa -->
<ul id="rows"></ul>
<script>
  function showRows(rows) {
    const items = rows.map((r) => {
      const item = document.createElement("li");
      item.textContent = `${r.title} (${r.count})`;
      return item;
    });
    document.getElementById("rows").replaceChildren(...items);
  }
  function setInsets(top, bottom) {
    document.body.style.paddingTop = `${top}px`;
    document.body.style.paddingBottom = `${bottom}px`;
  }
  function openRow(id) {
    const message = JSON.stringify({ type: "open", id: id });
    (window.webkit?.messageHandlers.host ?? window.Host).postMessage(message);
  }
</script>
```

Bu parça ölçülmez; model sayfanın kendisini çalıştırmaz. Satır başlığı sunucudan gelen veridir ve `textContent` onu işaretleme olarak değil, metin olarak yazar. Köprüye bağlı bir sayfada bu ayrım önemlidir: işaretleme olarak yorumlanan bir başlık sayfada betik çalıştırabilir ve köprü üzerinden kabuğa ileti gönderebilir. Parçanın son satırı ise ders açısından önemli bir ayrıntıyı gösterir: iletiyi kabuğa ulaştırmanın yolu iki platformda farklıdır ve sayfa bunu kendi içinde ayırır. Bu ayrım iletinin içeriğini değil, ulaştırma kanalını ilgilendirir; `ileti-bicimi` ortak kalır, çünkü kuralın baktığı şey ileti metnidir. Kanalın ayrımı ise ölçüm dışında kalır ve bakım bedeli olarak düzyazıda söylenir.

Kabuğun iOS tarafındaki payı sayfaya yazan parçası şöyle görünür.

```swift
// Platform kodu: HostViewController
import WebKit

@MainActor
func pushInsets(to webView: WKWebView) {
    let insets = webView.safeAreaInsets
    webView.evaluateJavaScript("setInsets(\(insets.top), \(insets.bottom))")
}
```

Bu parça da ölçülmez. Gösterdiği şey kabuğun sayfaya sayı verdiğidir; sayfa bu sayıyla ne yapacağını kendisi belirler, burada `setInsets` sayfanın üst ve alt boşluğunu bu paya göre ayarlar.

#### Kabuğun Verdiği Girdi

Sayfa her şeyi kendi bilgisiyle yapamaz. Ekranın çentik ve alt çubuk payı gibi bilgiyi aygıtın penceresi bilir. Web görünümü bu payın bir kısmını sayfaya kendisi aktarabilir; bu modelde ise pay kabuktan gelen bir girdidir. Kabuk bu payı sayfaya verir ve sayfa sabitlenmiş bir satırı bu payın altına yerleştirir.

**YA2.** Güvenli alan payı modelde `shell.mjs` dosyasındaki sabit bir tablodur: iOS için üst pay 47, alt pay 34; Android için üst pay 24, alt pay 0. Bu sayılar modelin girdisidir, bir aygıtın ölçüsü değildir; gerçek pay aygıta ve yönelime göre kabuğun her çağrıda bildirdiği değerdir.

```javascript
// shell.mjs — kabugun sayfaya verdigi guvenli alan payi
export function safeInset(platform) {
  return platform === "ios"
    ? { top: 47, bottom: 34 }
    : { top: 24, bottom: 0 };
}

export function flatInset() {
  return { top: 0, bottom: 0 };
}
```

```javascript
// page.mjs — sayfanin satiri yerlestiren islevi, kabugun payini disaridan alir
export function makePlacer(insetOf) {
  return function placeRow(row, platform) {
    const y = row.pinned ? insetOf(platform).top : row.index * 40;
    return `${row.title}:${y}`;
  };
}
```

`makePlacer` bir kabuk işlevi alır ve satırı yerleştiren işlevi döndürür. Sabitlenmiş satır payın altına oturur, öteki satırlar sırasına göre sıralanır. Aynı sayfa kodunu iki platformda ölçelim.

```javascript
// main.mjs
import { Sharing, countSharing } from "./sharing.mjs";
import { safeInset } from "./shell.mjs";
import { makePlacer } from "./page.mjs";

function renderRow(row) {
  return `<li>${row.title} (${row.count})</li>`;
}

const placeRow = makePlacer(safeInset);

const rows = [
  { index: 0, title: "Kahve", count: 2, pinned: false },
  { index: 1, title: "Haftalik plan", count: 7, pinned: true },
  { index: 2, title: "Market listesi", count: 0, pinned: false },
  { index: 3, title: "Yillik abonelik", count: 12, pinned: false },
];

const features = [
  new Sharing("satir-cizimi", { ios: renderRow, android: renderRow }, rows),
  new Sharing("satir-yerlesimi", { ios: placeRow, android: placeRow }, rows),
];
features.forEach((f) => f.line());
countSharing(features);
```

```text
satir-cizimi: kod=1 | fark=0/4 -> ortak
satir-yerlesimi: kod=1 | fark=1/4 -> dalli
ortak: 1
dalli: 1
ikiz: 0
tek: 0
```

`satir-yerlesimi` dallıdır. `kod=1` olduğu için sayfa kodu iki platformda aynıdır; ama sabitlenmiş tek satırda iki platformun sonucu ayrışır, çünkü kabuk iki platformda farklı pay verir. Öteki üç satır sırasına göre yerleşir ve fark üretmez. Yani dört örnekten yalnız biri dalı tetikledi.

Burada kayda değer olan, dalın sayfanın kodunda bulunmamasıdır. `makePlacer` hiçbir yerde "platform şuysa şunu yap" demez. Platformu kabuğun cevabı taşır ve sayfa onu olduğu gibi kullanır. Paylaşım bu yüzden sayfa kodunu değil, kabuğun verdiği girdiyi izler.

#### Yaygın Yanılgılar

Web görünümü tabanlı yaklaşımın etrafında üç yanılgı dolaşır ve ölçüm her birine somut bir karşılık verir.

Birincisi, "tek sayfa tek paylaşım demektir" düşüncesidir. Sayfa kodunun tek olması `kod=1` verir, ama paylaşımın ortak mı dallı mı olduğunu `fark` değeri belirler. Yukarıdaki ölçümde sayfa tekti ve yine de bir özellik dallıydı. Tek kod, özelliğin iki platformda aynı davrandığını garanti etmez; yalnız kodun bir yerde yazıldığını garanti eder.

İkincisi, "kabuk yok sayılabilir" düşüncesidir. Kabuk ince olsa da her platformda ayrı yazılır. Pay okuyan, iletiyi alan ve sayfayı yükleyen kod iki platformda iki ayrı yerel koddur. Kabuğun işlevleri kendi başlarına özellik olarak sınıflansaydı çoğu ikiz çıkardı. Yaklaşımın vaadi bu ikizleri sıfıra indirmek değil, küçültmektir: ikiz sayısı az ve her biri küçük kalır, ama bunu sağlayan kabuğun kendi disiplinidir.

Üçüncüsü, "sayfadan çağrılan her yetenek sayfa gibi ortaktır" düşüncesidir. Sayfa kamera, izin ya da dosya gibi bir yeteneği ileti yoluyla ister; isteği kuran satır ortaktır. Yeteneği karşılayan kod ise kabukta, her platformda ayrı yazılır ve bir platformda hiç bulunmayabilir. Aynı yetenek için sayfadaki çağrı ortak, kabuktaki karşılık ikiz, bir platformda eksikse tek çıkar. Hangi satırı ölçtüğünü söylemeden "bu özellik ortak" demek eksik bir cümledir. Yerel modül ihtiyacı konusu bu ayrımı ayrıca ele alır.

Üç yanılgının ortak yanı, düzeneğin vaadini bir paylaşım adıyla karıştırmaktır. Düzenek kodu bir yerde toplar; kodun her girdide aynı davranması, kabuğun verdiği girdilerin aynı olmasına bağlıdır.

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

Bu çıkarımı sınayalım. Aynı sayfa kodunu, aynı dört satırı ve aynı iki platformu alıyoruz; yalnız kabuğu değiştiriyoruz. İkinci kabuk iki platformda da sıfır pay verir.

```javascript
// main.mjs
import { Sharing, countSharing } from "./sharing.mjs";
import { safeInset, flatInset } from "./shell.mjs";
import { makePlacer } from "./page.mjs";

const withInset = makePlacer(safeInset);
const withoutInset = makePlacer(flatInset);

const rows = [
  { index: 0, title: "Kahve", count: 2, pinned: false },
  { index: 1, title: "Haftalik plan", count: 7, pinned: true },
  { index: 2, title: "Market listesi", count: 0, pinned: false },
  { index: 3, title: "Yillik abonelik", count: 12, pinned: false },
];

const features = [
  new Sharing("yerlesim-payli", { ios: withInset, android: withInset }, rows),
  new Sharing("yerlesim-paysiz", { ios: withoutInset, android: withoutInset }, rows),
];
features.forEach((f) => f.line());
countSharing(features);
```

```text
yerlesim-payli: kod=1 | fark=1/4 -> dalli
yerlesim-paysiz: kod=1 | fark=0/4 -> ortak
ortak: 1
dalli: 1
ikiz: 0
tek: 0
```

Sayfa kodu değişmedi; `makePlacer` iki ölçümde de aynı işlevdir ve iki ölçümde de `kod=1` çıktı. Kabuk pay verdiğinde dallı, vermediğinde ortak çıktı. İki ölçüm iki ayrı sayfa değildir; aynı sayfanın iki ayrı kabukla ölçülmesidir.

Bu sonuç yaklaşım kararında şu anlama gelir. Web görünümü tabanlı yaklaşımda sayfa kodunu tek tutmak yetmez; kabuğun sayfaya verdiği her girdi paylaşımı etkiler. Güvenli alan payı gibi girdiler arttıkça dallı özellikler artar. Kural bunu gösterir ama bedelini göstermez: bir payı yanlış okumanın bedeli yalnız ilgili aygıtta görünür ve sayfayı sınamak onu yakalamaz.

Yaklaşımın bir başka sınırı da kabuğun kendisidir. Uygulama mağazasına giden paket, izin istekleri ve yerel yetenekler kabukta durur. Bunlar sayfanın dışındaki özellikler olarak ayrı sınıflanır; sonraki derslerde ve platform gerçekleri konusunda yeniden ele alınırlar.

#### Özet

- Bu dersin paylaşımı: ortak. Sayfanın liste satırını çizen işlev iki platformda aynı işlevdir ve platformu okumaz.
- Web görünümü tabanlı yaklaşım arayüzü tek sayfadan çizer; kabuk her platformda ayrı, ince bir yerel parçadır.
- Sayfa kabuktan güvenli alan payı okuyunca aynı sayfa kodu dallı çıktı: dört örnekten yalnız sabitlenmiş satır ayrıştı.
- Aynı sayfa kodu pay vermeyen bir kabukla ortak çıktı: paylaşım sayfa kodunu değil, kabuğun verdiği girdiyi izler.
- Kural paylaşımın yapısını görür, bedelini görmez; kabuktan okunan her payın bedeli düzyazıda söylenir.

#### Sonraki Adım

Bu derste arayüz tek bir sayfadan çizildi ve sayfa yalnız kabukla konuştu. İkinci düzenek arayüzü sayfada çizmez; ortak kodun ürettiği bir ağacı her platformun yerel bileşenlerine eşler. Sıradaki ders, `Köprü Tabanlı Yaklaşım`, bu eşlemenin paylaşımını ölçer.

### Köprü Tabanlı Yaklaşım

Source: https://academia.sh/tr/kurslar/capraz-platform-yaklasimlari/kopru-tabanli-yaklasim

Köprü tabanlı yaklaşım arayüzü ortak kodla tanımlar ve her düğümü köprü üzerinden platformun yerel bileşenine eşler. Ders eşlemenin paylaşımını ölçer: aynı eşleme işlevi platforma göre farklı bileşen adı verir, eşleme iki ayrı işleve bölününce ikize döner.

Önceki ders arayüzü tek bir sayfadan çizen düzeneği ölçtü ve paylaşımın sayfa kodunu değil kabuğun verdiği girdiyi izlediğini gösterdi. İkinci düzenek arayüzü sayfada çizmez. Arayüzün tanımı ortak kodda durur; kod bir düğüm ağacı üretir ve bu ağacın her düğümü platformun kendi yerel bileşenine karşılık gelir. Düğüm ile bileşen arasındaki aracıya köprü denir.

Kursun sorusu değişmez: **bu özelliğin kodu platformlar arasında nasıl paylaşılıyor?** Bu derste soru, köprünün iki ucuna ayrı ayrı sorulur. Ortak uç tek koddur. Yerel uç ise her platformda ekranda gerçekten görünen bileşenlerdir.

Düzenek şöyle çalışır. Ortak kod bir düğüm ağacı kurar: bir düğme, bir etiket, bir giriş alanı, bir boşluk. Köprü bu ağacı platformun ağacına çevirir; her düğüm için hangi yerel bileşenin oluşturulacağını bir eşleme belirler. Ekranda görünen kontroller platformun kendi bileşenleridir. Bu yüzden görünüm ve davranış platformun beklentisine uyar. Ortak kodun yazdığı yalnız ağacın tanımıdır.

**YA1.** Model, bu paylaşımın kuralını kurar; model köprüyü, ortak kodu ya da yerel bileşenleri çalıştırmaz. Modelde köprünün eşlemesi bir tablodur: düğüm türü girer, platformun bileşen adı çıkar. Bileşen adları iOS için `UIButton` gibi, Android için `Button` gibi gerçek API adlarıdır ama model bu bileşenleri oluşturmaz, yalnız adlarını döndürür.

```javascript
// 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}`);
  }
}
```
Eşleme tablosunu ve eşleme işlevini ortak bir dosyaya koyuyoruz. Sonraki iki ölçüm onu içe alır.

```javascript
// bridge.mjs — kopru: dugum turunu platformun bilesen adina esler
export const NATIVE = {
  button: { ios: "UIButton", android: "Button" },
  label: { ios: "UILabel", android: "TextView" },
  input: { ios: "UITextField", android: "EditText" },
  spacer: { ios: "bosluk", android: "bosluk" },
};

export function mapToNative(node, platform) {
  return NATIVE[node][platform];
}
```

`spacer` düğümü iki platformda aynı adı alır: bir boşluk bileşen olarak değil, çevreleyen düzenin bir payı olarak çizilir. Öteki üç düğümün iki platformda ayrı bileşeni vardır.

#### Ortak Kod, Dallı Eşleme

Aynı ortak kod iki platforma gider. Köprünün düğüm türünü bileşen adına çeviren işlevi tek bir işlevdir; platformu argüman olarak alır. Ayrıca düğmeye dokunulduğunda ortak koda dönen olay adı iki platformda aynıdır: köprü iki platformun farklı olaylarını tek ada indirir.

```javascript
// main.mjs
import { Sharing, countSharing } from "./sharing.mjs";
import { mapToNative } from "./bridge.mjs";

function eventName(node) {
  return node === "button" ? "press" : "none";
}

const nodes = ["button", "label", "input", "spacer"];

const features = [
  new Sharing("bilesen-eslemesi", { ios: mapToNative, android: mapToNative }, nodes),
  new Sharing("olay-adi", { ios: eventName, android: eventName }, nodes),
];
features.forEach((f) => f.line());
countSharing(features);
```

```text
bilesen-eslemesi: kod=1 | fark=3/4 -> dalli
olay-adi: kod=1 | fark=0/4 -> ortak
ortak: 1
dalli: 1
ikiz: 0
tek: 0
```

`bilesen-eslemesi` dallıdır. Eşleme işlevi tektir, ama dört düğümün üçünde sonuç platforma göre ayrışır: düğme, etiket ve giriş alanı. Dördüncü düğüm, boşluk, ayrışmaz. Aynı kodun bir bölümü, ortak ucundan bakılınca, iki platformda iki ayrı bileşen üretiyor. `olay-adi` ise ortaktır, çünkü olay adı köprünün yukarı doğru tek tip bıraktığı bir arayüzdür.

Bu iki satır köprünün neyi paylaştırdığını gösterir. Ağacın tanımı ortak, bileşenin kimliği dallıdır. Ortak kod bir düğme ister; hangi düğmenin çıkacağına köprü ve platform karar verir. Yaklaşımın kazancı budur: ekranda platformun kendi kontrolü görünür. Bedeli de aynı yerden gelir: eşlemenin bilmediği bir düğüm türü ya da platformun kendine özgü bir özelliği ortak kodda ifade edilemez.

Ortak kodun ürettiği ağaç şöyle görünür. Parça ölçülmez; model ortak kodu çalıştırmaz.

```jsx
// Platform kodu: Cart
function Cart({ count, onPay }) {
  return (
    <View>
      <Text>Sepet: {count} urun</Text>
      <Button title="Ode" onPress={onPay} />
    </View>
  );
}
```

Bu ağaçta üç düğüm var ve her biri köprüden bir kez geçer. Köprü geçişi, ortak kod ile yerel taraf arasındaki bir iletişimdir; sıcak yolda sık tekrarlandığında iş birimi olarak önem kazanır. Başarım beklentileri konusu bunu ayrıca ele alır.

#### İki Ağaç, Bir Köprü

Köprü tabanlı yaklaşımda iki ağaç yaşar. Biri ortak kodun kurduğu düğüm ağacıdır; öteki platformun kendi yerel görünüm ağacıdır. Ortak kod bir durum değiştiğinde yeni düğüm ağacını üretir, köprü eskisiyle yenisini karşılaştırır ve yerel ağacı yalnız değişen düğümler için günceller. Her güncelleme bir köprü geçişidir: ortak taraftan yerel tarafa bir ileti gider.

Bu düzeneğin iki sonucu var. Birincisi, kullanıcı etkileşimi yerel bileşende başlar, olay köprüden ortak koda gelir ve ortak kodun yanıtı yine köprüden yerel tarafa döner. İki yönlü geçiş bir ekranın yanıt verme biçimini belirler; ekip bu yüzden sık güncellenen yerleri, kaydırma ve yazı yazma gibi, ayrıca inceler. İkincisi, yerel ağaç platformun kendi ağacı olduğu için erişilebilirlik, odak ve yazı girişi gibi davranışlar platformun kendi bileşenine bırakılır. Ortak kod bu davranışları yazmaz ve tekrar etmez.

Model bu iki ağacı tek bir eşleme olarak kurar. Gerçek köprüde bir düğümün birçok özelliği vardır: renk, boyut, metin, kenar boşluğu. Her özellik ayrı eşlenebilir ve her biri ayrı bir paylaşım sorusudur. Bir özelliğin iki platformda aynı sonucu vermesi ya da vermemesi, bu özelliğin eşlemesinin ortak mı dallı mı olduğunu söyler. Yaklaşım bu yüzden tek bir paylaşım değil, özellik özellik bir paylaşım dağılımı üretir.

#### Dallı, Yineleme Değildir

Önceki kurslardan birinde, M30/K05'te, bir işlemin yinelenmesinin ya da sırasının değişmesinin sonucu değiştirip değiştirmediğini sormuştuk. Buradaki dallı o soru değildir. Dallı, sonucun girdinin yinelenmesiyle ya da sırasıyla değil, platformun değişmesiyle değişmesidir. Bunu sınamak için aynı eşlemeyi üç ayrı örnek listesiyle ölçelim: düz sıra, ters sıra ve her düğümün iki kez yinelendiği liste.

```javascript
// main.mjs
import { Sharing, countSharing } from "./sharing.mjs";
import { mapToNative } from "./bridge.mjs";

const nodes = ["button", "label", "input", "spacer"];
const reversed = [...nodes].reverse();
const doubled = [...nodes, ...nodes];

const features = [
  new Sharing("eslem-sirali", { ios: mapToNative, android: mapToNative }, nodes),
  new Sharing("eslem-ters", { ios: mapToNative, android: mapToNative }, reversed),
  new Sharing("eslem-yinelenmis", { ios: mapToNative, android: mapToNative }, doubled),
];
features.forEach((f) => f.line());
countSharing(features);
```

```text
eslem-sirali: kod=1 | fark=3/4 -> dalli
eslem-ters: kod=1 | fark=3/4 -> dalli
eslem-yinelenmis: kod=1 | fark=6/8 -> dalli
ortak: 0
dalli: 3
ikiz: 0
tek: 0
```

Sıra değişti, `fark` aynı kaldı: üç düğümün sonucu platforma göre ayrışmaya devam etti. Liste iki katına çıktı, `fark` 3/4'ten 6/8'e geçti: oran aynıdır, çünkü her düğüm yinelendiğinde platform farkı da yinelendi. Üç ölçümün üçü de dallıdır. Bu, dallı paylaşımın sebebinin girdinin yinelenmesi ya da sırası olmadığını gösterir; sebep platformun kendisidir.

**YA2.** Düğüm türleri ve bileşen adları modelin girdisidir. Gerçek bir köprü, bir düğüm türünü birden fazla bileşenle eşleyebilir ve eşlemeyi sürüme göre değiştirebilir; model bunları tek bir tabloya indirir.

#### Yaygın Yanılgılar

Köprü tabanlı yaklaşım hakkında iki yanılgı sık görülür. Birincisi, "ortak kod varsa ekranlar iki platformda aynı görünür" düşüncesidir. Ekranda görünen bileşen platformun kendi bileşenidir; bu yüzden ayrışır. Köprü bu ayrışmayı gizlemez, ortak koddan saklar. Ortak kod hangi bileşenin çizildiğini bilmez ve bilmediği için o bileşenin davranışını da denetleyemez.

İkincisi, "dallı kötü, ortak iyidir" düşüncesidir. Dallı bu yaklaşımın amacıdır: kullanıcı iki platformda kendi platformunun bileşenini görmek ister. Paylaşımı mümkün olduğunca artırmak her özellik için doğru hedef değildir. Kural, dallılığı bir hata olarak değil bir yapı olarak sınıflar; hangi özelliğin dallı kalması gerektiğine ekip karar verir.

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

Aynı eşleme bu kez iki ayrı işleve bölünüyor. Biri iOS'un bileşen adlarını, öteki Android'inkini döndürür; her işlev kendi tablosunu okur. Sonuçlar bir önceki ölçümdekiyle aynıdır: yalnız işlev sayısı iki olur.

```javascript
// main.mjs
import { Sharing, countSharing } from "./sharing.mjs";
import { NATIVE, mapToNative } from "./bridge.mjs";

function mapIos(node) {
  return NATIVE[node].ios;
}

function mapAndroid(node) {
  return NATIVE[node].android;
}

const nodes = ["button", "label", "input", "spacer"];

const features = [
  new Sharing("eslem-tek-islev", { ios: mapToNative, android: mapToNative }, nodes),
  new Sharing("eslem-iki-islev", { ios: mapIos, android: mapAndroid }, nodes),
];
features.forEach((f) => f.line());
countSharing(features);
```

```text
eslem-tek-islev: kod=1 | fark=3/4 -> dalli
eslem-iki-islev: kod=2 | fark=3/4 -> ikiz
ortak: 0
dalli: 1
ikiz: 1
tek: 0
```

İki ölçümün `fark` değeri aynıdır: üç düğümün sonucu iki platformda ayrışır. Değişen yalnız `kod` oldu, 1'den 2'ye. Kural sonucu görmez, kodun sayısını görür; sonuçlar aynı kaldığı hâlde paylaşım dallıdan ikize döndü.

Bu sınır bir uyarıdır. Eşleme iki ayrı işleve ya da iki ayrı dosyaya bölününce aynı ekran aynı bileşenleri üretmeye devam eder, ama artık iki kod bakım ister. Yeni bir düğüm türü eklenecek olduğunda iki işlev ayrı ayrı güncellenir ve biri unutulduğunda platformlar ayrışır. Dallı bir eşlemede bu risk yoktur, çünkü tek tablo iki platformu birlikte güncelleyen yerdir. Kural bu bakım farkını görmez; ders görür ve düzyazıda söyler.

#### Özet

- Bu dersin paylaşımı: dallı. Düğüm türünü platformun bileşen adına eşleyen tek işlev, dört düğümün üçünde iki platformda farklı ad verdi.
- Köprü tabanlı yaklaşımda ağacın tanımı ortak, bileşenin kimliği platforma göre değişir; ekranda platformun kendi bileşeni görünür.
- Eşleme sıralanınca ya da yinelenince `fark` oranı değişmedi: dallı, platformun değişmesinden doğar, yinelemeden ya da sıradan değil.
- Aynı eşleme iki ayrı işleve bölününce sonuçlar aynı kaldı ama paylaşım ikize döndü: kural kodun sayısını görür.
- Kural bakım farkını görmez; iki işlevin birinin unutulması riski düzyazıda söylenir.

#### Sonraki Adım

Bu derste arayüzü platformun kendi bileşenleri çizdi. Üçüncü düzenek hiçbir yerel bileşen kullanmaz: kendi çizim motoruyla her şeyi bir tuvale çizer. Sıradaki ders, `Kendi Çizim Motoruyla Yaklaşım`, bu düzenekte düzen işlevinin paylaşımını ölçer.

### Kendi Çizim Motoruyla Yaklaşım

Source: https://academia.sh/tr/kurslar/capraz-platform-yaklasimlari/kendi-cizim-motoruyla-yaklasim

Kendi çizim motoruyla çalışan yaklaşım platformun yerel bileşenlerini kullanmaz; arayüzü kendi motoruyla bir tuvale çizer. Ders düzen, boyama sırası ve dokunma çözümlemesinin paylaşımını ölçer ve metin genişliğinin platformdan gelmesinin düzeni nasıl dallandırdığını gösterir.

Önceki ders ortak kodun bir düğüm ağacı kurduğunu ve köprünün bu ağacı platformun kendi bileşenlerine eşlediğini gösterdi; paylaşım dallı çıktı, çünkü bileşenin kimliği platforma aitti. Üçüncü düzenek bu eşlemeyi tümüyle kaldırır: platformun yerel bileşenleri kullanılmaz. Uygulama, platformun verdiği tek bir yüzeye, tuvale, kendi çizim motoruyla her şeyi çizer.

Kursun sorusu değişmez: **bu özelliğin kodu platformlar arasında nasıl paylaşılıyor?** Çizim motoru bu soruya cesur bir yanıt verir. Düğme, liste, metin alanı ve kaydırma çubuğu motorun kendi kodudur; platformun karşılığı yoktur. Kod bu yüzden en geniş biçimde ortak olabilir.

Düzenek şöyle çalışır. Uygulama bir bileşen ağacı kurar; ağaçtaki her öğe bir düzen kuralıyla boyutlandırılır ve konumlandırılır. Motor ağacı gezer, her öğeyi tuvale çizer ve platformdan temel olarak girdileri alır: dokunma noktaları, tuvalin boyutu ve kare başına bir sinyal. Erişilebilirlik, yaşam döngüsü ve platform iletileri de platform tarafından sağlanır. Yerel bileşen olmadığı için platformun kendi düğmesinin görünüşü ya da davranışı kullanılmaz; motor bunları kendisi taklit eder ya da kendi tasarımını çizer.

**YA1.** Model, bu paylaşımın kuralını kurar; model çizim motorunu, tuvali ya da bir kareyi çalıştırmaz. Modelde motorun düzen işlevi kutu genişliklerinden satırlar üretir. Kapsayıcı genişliği 100'dür ve bu sayı modelin girdisidir; bir aygıtın ekran ölçüsü değildir.

```javascript
// 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}`);
  }
}
```
#### Düzen: Kutuların Konumu

Düzenin en basit hâli satır kırmadır. Kutular sırayla soldan sağa dizilir; bir sonraki kutu kapsayıcıya sığmıyorsa yeni bir satır başlar. İşlev kutu genişliklerinden satırlar üretir ve platformu hiç okumaz. Aynı bloğa boyama sırasını da koyuyoruz: katmanlar z değerine göre sıralanır, küçük z önce boyanır.

```javascript
// main.mjs
import { Sharing, countSharing } from "./sharing.mjs";

function layoutBoxes(widths) {
  const rows = [];
  let row = [];
  let used = 0;
  for (const w of widths) {
    if (row.length > 0 && used + w > 100) {
      rows.push(row);
      row = [];
      used = 0;
    }
    row.push(w);
    used += w;
  }
  rows.push(row);
  return rows;
}

function paintOrder(layers) {
  return [...layers].sort((a, b) => a.z - b.z).map((l) => l.name).join(">");
}

const boxSets = [[30, 40, 20], [60, 50], [100], [45, 45, 45]];
const layerSets = [
  [{ name: "metin", z: 2 }, { name: "zemin", z: 0 }, { name: "golge", z: 1 }],
  [{ name: "dugme", z: 1 }, { name: "zemin", z: 0 }, { name: "ipucu", z: 3 }],
  [{ name: "liste", z: 1 }, { name: "zemin", z: 0 }, { name: "baslik", z: 2 }],
];

const features = [
  new Sharing("kutu-yerlesimi", { ios: layoutBoxes, android: layoutBoxes }, boxSets),
  new Sharing("boyama-sirasi", { ios: paintOrder, android: paintOrder }, layerSets),
];
features.forEach((f) => f.line());
countSharing(features);
```

```text
kutu-yerlesimi: kod=1 | fark=0/4 -> ortak
boyama-sirasi: kod=1 | fark=0/3 -> ortak
ortak: 2
dalli: 0
ikiz: 0
tek: 0
```

İki özellik de ortaktır. `kutu-yerlesimi` için `kod=1` ve `fark=0/4` çıktı: dört kutu kümesinin hiçbirinde iki platform farklı satır üretmedi. Düzen motorun içinde hesaplanır ve girdisi yalnız kutu genişlikleridir. Aynı bileşen ağacı iki platformda aynı yerleşimi verir; bu yaklaşımın en güçlü vaadidir. Düzenin platformdan bağımsız olması, bileşenin kendi düzeninden doğan platforma özgü hataların ortadan kalkması demektir; yazı tipi gibi platformdan gelen girdiler bunun dışındadır. Bir düzen hatası düzeltildiğinde iki platform birlikte düzelir.

Motoru kodda kullanan taraf, tuvale çizim işini şöyle yazar. Parça ölçülmez; model motoru çalıştırmaz.

```dart
// Platform kodu: LabelPainter
import 'package:flutter/widgets.dart';

class LabelPainter extends CustomPainter {
  LabelPainter(this.text);
  final String text;

  @override
  void paint(Canvas canvas, Size size) {
    final painter = TextPainter(
      text: TextSpan(text: text, style: const TextStyle(color: Color(0xFF202020))),
      textDirection: TextDirection.ltr,
    )..layout(maxWidth: size.width);
    painter.paint(canvas, Offset.zero);
  }

  @override
  bool shouldRepaint(LabelPainter old) => old.text != text;
}
```

Parçanın kritik satırı `layout(maxWidth: size.width)` satırıdır. Metnin ne kadar yer kaplayacağı burada hesaplanır ve bu hesap, motorun tuvale çizeceği şeyi belirler. Bu hesabın girdisi neyden gelir sorusu, dersin sınırlayıcı ölçümünde yeniden karşımıza çıkacak.

#### Girdi ve Kaydırma Motorda

Motor yalnız çizmez; dokunmayı da kendi çözümler. Platform dokunma noktasını verir, hangi öğeye dokunulduğunu motor bulur. Kaydırma konumunu da motor sınırlar: içerik bir görünüm kadar uzunsa konum sıfırla içerik farkı arasında kalır. İki işlev de platformu okumaz.

**YA2.** Sabit kutular modelde dört bölgedir. İçerik yüksekliği 300, görünüm yüksekliği 100'dür; bu sayılar modelin girdisidir ve kaydırma konumu 0 ile 200 arasında kalır.

```javascript
// main.mjs
import { Sharing, countSharing } from "./sharing.mjs";

const regions = [
  { x: 0, y: 0, w: 50, h: 40 },
  { x: 50, y: 0, w: 50, h: 40 },
  { x: 0, y: 40, w: 100, h: 60 },
];

function hitTest(point) {
  const [px, py] = point;
  return regions.findIndex(
    (r) => px >= r.x && px < r.x + r.w && py >= r.y && py < r.y + r.h,
  );
}

function clampScroll(offset) {
  return Math.min(Math.max(offset, 0), 200);
}

const points = [[5, 5], [60, 10], [30, 70], [200, 5]];
const offsets = [-10, 0, 120, 500];

const features = [
  new Sharing("dokunma-hedefi", { ios: hitTest, android: hitTest }, points),
  new Sharing("kaydirma-siniri", { ios: clampScroll, android: clampScroll }, offsets),
];
features.forEach((f) => f.line());
countSharing(features);
```

```text
dokunma-hedefi: kod=1 | fark=0/4 -> ortak
kaydirma-siniri: kod=1 | fark=0/4 -> ortak
ortak: 2
dalli: 0
ikiz: 0
tek: 0
```

Böylece dört özellik ortak çıktı: yerleşim, boyama sırası, dokunma hedefi ve kaydırma sınırı. Dördü de motorun içindedir ve dördü de platformu okumaz. Yaklaşımın ortaklık vaadi, motorun yerel bileşenleri gerçekten bıraktığı ölçüde tutar.

Bu vaadin bedeli, platformun kendi bileşeninden gelen şeylerin kaybıdır. Bir yerel metin alanının erişilebilirlik davranışı, klavye düzeni ve seçim menüsü platformun işidir. Motor bunları kendi çizdiği bir alanda taklit etmek zorundadır; taklit eksik kalırsa kullanıcı bunu hisseder. Kural bu eksikliği görmez, çünkü eksiklik bir paylaşım farkı olarak değil bir davranış farkı olarak yaşanır.

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

Düzen platformu okumaz dedik, ama düzenin girdisi yalnız kutu genişlikleri midir? Metin bir kutudur ve kutunun genişliği, kullanılan yazı tipinin karakter ölçüsünden gelir. Yazı tipi motorla birlikte gelen bir yazı tipiyse ölçü iki platformda aynıdır. Platformun kendi yazı tipi kullanılıyorsa ölçü platforma göre değişir.

**YA3.** Karakter genişliği modelde bir girdidir: motorun kendi yazı tipinde 6, iOS'un sistem yazı tipinde 6, Android'in sistem yazı tipinde 7. Gerçek yazı tipi ölçüleri karakterden karaktere değişir ve bu üç sayı tek bir temsil değeridir. Kapsayıcı genişliği 60'tır; bu durumda satır başına karakter sayısı iOS'ta 10, Android'de 8 olur.

```javascript
// text.mjs — metni karakter genisligine gore satirlara boler
export function glyphWidth(platform) {
  return platform === "ios" ? 6 : 7;
}

export function bundledWidth() {
  return 6;
}

export function makeWrap(widthOf) {
  return function wrapText(text, platform) {
    const perLine = Math.floor(60 / widthOf(platform));
    const lines = [];
    let line = "";
    for (const word of text.split(" ")) {
      const next = line === "" ? word : `${line} ${word}`;
      if (line !== "" && next.length > perLine) {
        lines.push(line);
        line = word;
      } else {
        line = next;
      }
    }
    lines.push(line);
    return lines.join("/");
  };
}
```

Aynı dört metni iki kez ölçelim: bir kez motorla gelen yazı tipinin genişliğiyle, bir kez platformun yazı tipinin genişliğiyle. Kırma işlevi aynıdır; değişen yalnız genişliğin nereden geldiğidir.

```javascript
// main.mjs
import { Sharing, countSharing } from "./sharing.mjs";
import { glyphWidth, bundledWidth, makeWrap } from "./text.mjs";

const bundledWrap = makeWrap(bundledWidth);
const platformWrap = makeWrap(glyphWidth);

const texts = ["Kahve", "Cay kahve", "Haftalik plan", "Su ve cay"];

const features = [
  new Sharing("yazi-motor-fontu", { ios: bundledWrap, android: bundledWrap }, texts),
  new Sharing("yazi-platform-fontu", { ios: platformWrap, android: platformWrap }, texts),
];
features.forEach((f) => f.line());
countSharing(features);
```

```text
yazi-motor-fontu: kod=1 | fark=0/4 -> ortak
yazi-platform-fontu: kod=1 | fark=2/4 -> dalli
ortak: 1
dalli: 1
ikiz: 0
tek: 0
```

İki ölçümde de `kod=1` ve motor aynıdır. Motorla gelen yazı tipinde dört metin iki platformda aynı satırlara bölündü ve paylaşım ortak çıktı. Platformun yazı tipinde ise iki metin ayrıştı: "Cay kahve" ve "Su ve cay". Dokuz karakterlik bu iki metin, iOS'ta on karakterlik satıra sığar, Android'de sekiz karakterlik satıra sığmaz ve ikiye bölünür. "Haftalik plan" ise iki platformda da ikiye bölünür, çünkü on üç karakter iki sınırı da aşar; sonuç aynı kalır.

Motor her pikseli kendisi çizse de girdisinin bir kısmı platformdan gelir. Yazı tipi seçimi gibi bir girdi platforma bağlandığında düzen, yerleşimin kendisini değiştirmeden dallanır. Bu yaklaşımda paylaşımı korumanın yolu, platformdan girdi almayı en aza indirmektir; motorla gelen yazı tipi bu yüzden ortak düzenin ön koşuludur. Kural, girdinin nereden geldiğini görmez; yalnız sonucun ayrıştığını görür ve ayrışmanın sebebini düzyazı söyler.

#### Özet

- Bu dersin paylaşımı: ortak. Kutuların konumunu hesaplayan düzen işlevi platformu okumaz ve iki platformda dört kutu kümesinde aynı yerleşimi verdi.
- Kendi çizim motoruyla çalışan yaklaşımda düzen, boyama sırası, dokunma hedefi ve kaydırma sınırı motorun içindedir; dördü de ortak çıktı.
- Metin genişliği platformun yazı tipi ölçüsünden gelince aynı düzen işlevi dört metnin ikisinde farklı satır kırdı ve paylaşım dallıya döndü.
- Motorla gelen yazı tipinde aynı metinler ortak çıktı: platformdan girdi almak, paylaşımı kodu değiştirmeden dallandırır.
- Kural paylaşımın yapısını görür, bedelini görmez; yerel bileşenin erişilebilirlik ve seçim davranışlarının kaybı düzyazıda söylenir.

#### Sonraki Adım

Bu derste platformun yerel bileşenleri bütünüyle bırakıldı ve paylaşım en geniş hâline vardı. Dördüncü düzenek ters yönde gider: yalnız iş kurallarını ortaklaştırır, arayüzü her platformda yerel yazar. Sıradaki ders bu düzenekte arayüzün paylaşımını ölçer.

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

Source: https://academia.sh/tr/kurslar/capraz-platform-yaklasimlari/paylasilan-mantik-yerel-arayuz

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.

Ö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.

```javascript
// 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.

```javascript
// 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.

```javascript
// 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);
```

```text
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 **ikiz**dir. 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.

```swift
// 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) }
        }
    }
}
```

```kotlin
// 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.

```javascript
// 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);
```

```text
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 **ikiz**dir, çü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.

```javascript
// 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);
```

```text
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.

### Yaklaşım Seçim Ölçütleri

Source: https://academia.sh/tr/kurslar/capraz-platform-yaklasimlari/yaklasim-secim-olcutleri

Yaklaşım seçimi özelliklerin platforma bağımlılığına bakar. Ders aynı altı özelliği tek kod tabanı ve paylaşılan mantık ile yerel arayüz düzenekleriyle ölçer, dört paylaşımın iki dağılımını karşılaştırır ve sayının yaklaşımı tek başına seçmediğini gösterir.

Beş ders beş düzeneği tek tek ölçtü. Her düzeneğin bir paylaşımı çıktı: sayfa ortak, köprü dallı, çizim motoru ortak, arayüzü ayrı yazılan düzenek ikiz. Gerçek bir uygulamada ise tek bir özellik yoktur. Bir liste vardır ve listenin her satırı kendi paylaşımını taşır. Yaklaşım seçimi bu listenin ne kadarının hangi paylaşıma düştüğüne bakar.

Kursun sorusu değişmez: **bu özelliğin kodu platformlar arasında nasıl paylaşılıyor?** Bu derste soru aynı altı özelliğe iki düzenekte sorulur ve iki dağılım yan yana konur. Ders bir yaklaşımı önermez. Önerinin neye dayanması gerektiğini gösterir.

Seçimin ilk girdisi, özelliğin platforma ne kadar bağlı olduğudur. Fiyat biçimi platformu hiç okumaz. Başlık kısaltma, yazı alanının genişliğine göre platformdan etkilenir. Gezinme çubuğu platformun kendi alışkanlığına uyar. Biyometrik giriş platformun kendi programlama arayüzünü çağırır. Bir ana ekran parçası ise bu uygulamada yalnız bir platform için yazılır. Altı özellik, platforma bağımlılığın altı farklı derecesidir.

**YA1.** Model, bu paylaşımın kuralını kurar; model bir uygulamayı derlemez, bir ekranı çizmez ve bir platformun programlama arayüzünü çağırmaz. Altı özellik modelde sabit bir listedir. Uygulamada "ana ekran parçası" yalnız iOS için vardır; Android karşılığı bu uygulamanın kapsamında değildir. Bu bir varsayımdır ve gerçek bir ürün kararı değildir.

```javascript
// 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}`);
  }
}
```
Altı özelliğin işlevlerini ortak bir dosyaya koyuyoruz. Sonraki iki ölçüm onları farklı düzeneklerle birleştirir.

```javascript
// catalog.mjs — alti ozelligin islevleri
export function priceText(cents) {
  const pad = String(cents % 100).padStart(2, "0");
  return `${Math.floor(cents / 100)}.${pad}`;
}

export function cartSum(lines) {
  return lines.reduce((sum, [price, qty]) => sum + price * qty, 0);
}

export function titleCut(title, platform) {
  const limit = platform === "ios" ? 10 : 8;
  return title.length > limit ? `${title.slice(0, limit - 1)}~` : title;
}

export function backBar(screen, platform) {
  return `${screen}:${platform === "ios" ? "sol" : "alt"}-geri`;
}

export const iosBiometric = (user) => `yuz:${user}`;
export const androidBiometric = (user) => `parmak:${user}`;
export const iosTile = (name) => `parca:${name}`;
```

`titleCut` ve `backBar` platform bağımsız yazılmıştır ama platform adını okurlar. Bu iki işlev ölçümde tek işlev olarak durur. Biyometrik girişin iki işlevi ve ana ekran parçasının tek işlevi ayrı ayrı adlandırılmıştır.

#### Tek Olan Özellik

İlk ölçümde iki uçtaki özelliği koyuyoruz: platforma hiç bağlı olmayan fiyat biçimi ve bu uygulamada yalnız bir platform için yazılan ana ekran parçası. İlki ortak çıkacaktır. İkincisi için Android uygulaması yoktur ve `impl` içinde yalnız `ios` anahtarı bulunur.

```javascript
// main.mjs
import { Sharing, countSharing } from "./sharing.mjs";
import { priceText, iosTile } from "./catalog.mjs";

const prices = [1250, 300, 99999, 5];
const tiles = ["gunluk", "haftalik"];

const features = [
  new Sharing("fiyat-bicimi", { ios: priceText, android: priceText }, prices),
  new Sharing("ana-ekran-parcasi", { ios: iosTile }, tiles),
];
features.forEach((f) => f.line());
countSharing(features);
```

```text
fiyat-bicimi: kod=1 | fark=0/4 -> ortak
ana-ekran-parcasi: kod=1 | eksik=android -> tek
ortak: 1
dalli: 0
ikiz: 0
tek: 1
```

`fiyat-bicimi` için `fark=0/4` ve `kod=1` çıktı: bu özellik bir kez yazılır ve iki platformda aynı sonucu verir. `ana-ekran-parcasi` için `fark` yerine `eksik=android` okunur ve paylaşım **tek**tir. Tek olan bir özellikte karşılaştırılacak ikinci bir uygulama yoktur, bu yüzden `fark` hesaplanmaz. Hangi yaklaşım seçilirse seçilsin bu özellik tek kalır, çünkü eksik olan şey kodun düzeneği değil, öbür platformdaki uygulamanın kendisidir.

Yalnız iOS'ta bulunan bir özelliğin kaynağı şöyle görünür. Parça ölçülmez.

```swift
// Platform kodu: HomeTile
import WidgetKit
import SwiftUI

struct HomeTile: Widget {
    var body: some WidgetConfiguration {
        StaticConfiguration(kind: "home-tile", provider: TileProvider()) { entry in
            Text(entry.title)
        }
    }
}
```

Bu, kapsam kararıyla tek platformda kalan bir özelliktir. Hiçbir ortak katman, öbür platformda yazılmamış bir uygulamayı kendiliğinden ortaya çıkarmaz. Seçimin ikinci girdisi buradan çıkar: **hangi özellikler yalnız bir platform için yazılıyor?** Bu özellikler yaklaşım karşılaştırmasının dışında tutulur, çünkü yaklaşımlar arasında farkları yoktur.

#### Tek Kod Tabanında Altı Özellik

İkinci ölçümde altı özelliği tek kod tabanı düzeneğiyle ölçüyoruz. Bu düzenekte her özellik mümkün olduğunca tek işlevle yazılır; platforma bağımlı kısımlar işlevin içinde `platform` anahtarıyla ayrılır. İstisna, biyometrik girişin platform programlama arayüzünü çağırması ve ana ekran parçasının tek platformda bulunmasıdır.

**YA2.** Modelde "tek kod tabanı" şu anlama gelir: platforma duyarlı özellikler tek işlev olarak kalır, `platform` bağımsız değişkenini okur. Biyometrik giriş bu düzenekte bile iki ayrı işlev ister, çünkü iki platformun programlama arayüzü ortak değildir. Bu model bir kabuk bir köprüyü ya da bir motoru taklit etmez; yalnız işlevlerin sayısını sayar.

```javascript
// main.mjs
import { Sharing, countSharing } from "./sharing.mjs";
import {
  priceText, cartSum, titleCut, backBar, iosBiometric, androidBiometric, iosTile,
} from "./catalog.mjs";

const prices = [1250, 300, 99999, 5];
const carts = [[[100, 2], [250, 1]], [[999, 3]], [[50, 4], [75, 2]]];
const titles = ["Kahve", "Cay kahve", "Haftalik plan", "Su"];
const screens = ["liste", "detay", "sepet"];
const users = ["ada", "can", "eda"];
const tiles = ["gunluk", "haftalik"];

const features = [
  new Sharing("fiyat-bicimi", { ios: priceText, android: priceText }, prices),
  new Sharing("sepet-toplami", { ios: cartSum, android: cartSum }, carts),
  new Sharing("baslik-kisaltma", { ios: titleCut, android: titleCut }, titles),
  new Sharing("gezinme-cubugu", { ios: backBar, android: backBar }, screens),
  new Sharing("biyometrik-giris", { ios: iosBiometric, android: androidBiometric }, users),
  new Sharing("ana-ekran-parcasi", { ios: iosTile }, tiles),
];
features.forEach((f) => f.line());
countSharing(features);
```

```text
fiyat-bicimi: kod=1 | fark=0/4 -> ortak
sepet-toplami: kod=1 | fark=0/3 -> ortak
baslik-kisaltma: kod=1 | fark=2/4 -> dalli
gezinme-cubugu: kod=1 | fark=3/3 -> dalli
biyometrik-giris: kod=2 | fark=3/3 -> ikiz
ana-ekran-parcasi: kod=1 | eksik=android -> tek
ortak: 2
dalli: 2
ikiz: 1
tek: 1
```

Dağılım iki ortak, iki dallı, bir ikiz ve bir tektir. İlk iki özellik platformu hiç okumadığı için ortaktır. `baslik-kisaltma` ve `gezinme-cubugu` tek işlevdir ama platform anahtarını okur; dallı çıkmalarının sebebi budur. `baslik-kisaltma` için `fark=2/4` okunur: dört başlığın ikisinde iki platform farklı kısaltma üretti, çünkü iOS'ta yer on karakter, Android'de sekiz karakterdir. Bu iki sayı modelin girdisidir ve gerçek bir yazı alanının ölçüsü değildir. `biyometrik-giris` ikiz çıktı, çünkü iki platform iki ayrı programlama arayüzünü çağırır ve bu çağrıyı yazan iki kod vardır.

Bu dağılım, tek kod tabanının vaadinin hem gücünü hem sınırını gösterir. Altı özelliğin dördü tek işlevle yazıldı, ama dördünün ikisi hâlâ dallı. Tek işlev olmak, tek davranış olmak demek değildir.

#### Paylaşılan Mantık ve Yerel Arayüzle Altı Özellik

Üçüncü ölçümde aynı listeyi öteki düzenekle ölçüyoruz. Bu düzenekte iş kuralları ortak modülde kalır: fiyat biçimi ve sepet toplamı. Başlık, gezinme çubuğu ve biyometrik giriş arayüz katmanına aittir ve her platformda ayrı bir işlev olarak yazılır.

**YA3.** Modelde arayüz işlevleri ortak işlevi platform adıyla sarar: `iosTitle` ve `androidTitle` aynı kısaltma kuralını kendi platform adıyla çağırır, `iosBack` ve `androidBack` aynı gezinme kuralını. İki sarmalayıcı ayrı işlev nesneleridir; bu, ikinin ayrı yazıldığını temsil eder. Gerçek ekiplerde iki arayüz işlevi ortak bir kuralı sarmak zorunda değildir ve çoğunlukla sarmaz.

```javascript
// main.mjs
import { Sharing, countSharing } from "./sharing.mjs";
import {
  priceText, cartSum, titleCut, backBar, iosBiometric, androidBiometric, iosTile,
} from "./catalog.mjs";

const iosTitle = (title) => titleCut(title, "ios");
const androidTitle = (title) => titleCut(title, "android");
const iosBack = (screen) => backBar(screen, "ios");
const androidBack = (screen) => backBar(screen, "android");

const prices = [1250, 300, 99999, 5];
const carts = [[[100, 2], [250, 1]], [[999, 3]], [[50, 4], [75, 2]]];
const titles = ["Kahve", "Cay kahve", "Haftalik plan", "Su"];
const screens = ["liste", "detay", "sepet"];
const users = ["ada", "can", "eda"];
const tiles = ["gunluk", "haftalik"];

const features = [
  new Sharing("fiyat-bicimi", { ios: priceText, android: priceText }, prices),
  new Sharing("sepet-toplami", { ios: cartSum, android: cartSum }, carts),
  new Sharing("baslik-kisaltma", { ios: iosTitle, android: androidTitle }, titles),
  new Sharing("gezinme-cubugu", { ios: iosBack, android: androidBack }, screens),
  new Sharing("biyometrik-giris", { ios: iosBiometric, android: androidBiometric }, users),
  new Sharing("ana-ekran-parcasi", { ios: iosTile }, tiles),
];
features.forEach((f) => f.line());
countSharing(features);
```

```text
fiyat-bicimi: kod=1 | fark=0/4 -> ortak
sepet-toplami: kod=1 | fark=0/3 -> ortak
baslik-kisaltma: kod=2 | fark=2/4 -> ikiz
gezinme-cubugu: kod=2 | fark=3/3 -> ikiz
biyometrik-giris: kod=2 | fark=3/3 -> ikiz
ana-ekran-parcasi: kod=1 | eksik=android -> tek
ortak: 2
dalli: 0
ikiz: 3
tek: 1
```

Dağılım iki ortak, sıfır dallı, üç ikiz ve bir tek oldu. İlk ölçümde dallı olan iki özellik burada ikize döndü: sonuçları değişmedi, `fark=2/4` ve `fark=3/3` aynı kaldı, ama her platformun kendi işlevi olduğu için kod sayısı ikiye çıktı. Ortak sayısı ve tek sayısı değişmedi. Altı özelliğin yarısı bu düzenekte iki kez yazılmıştır.

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

İki dağılımı yan yana koyunca bir sonuç çıkarmak çekicidir: ilkinde iki dallı ve bir ikiz var, ikincisinde sıfır dallı ve üç ikiz var; öyleyse biri öteki kadar iyi ya da kötü. Kural bunu söyleyemez. Sayılar iki düzeneğin altı özellik üzerindeki paylaşımını verir. Hangisinin daha az bakım isteyeceğini, hangisinin daha az hataya yol açacağını ya da hangisinin kullanıcıya daha iyi göründüğünü vermez.

Dallı bir özellik tek işlevdir ama içinde bir dal vardır; dalın iki kolu da bakım ister ve kolların ayrışması izlenmelidir. İkiz bir özellik iki işlevdir ve ikisi de bakım ister. Hangisi daha pahalıdır sorusunun yanıtı, özelliğin ne sıklıkla değiştiğine ve değişikliğin iki platforma birlikte gidip gitmediğine bağlıdır. Kural bu bilgiyi görmez; yalnız kodun yapısını görür.

Seçim bu yüzden en az dört girdiye bakar ve ölçüm yalnız ilkine yardım eder:

- Özelliklerin hangi paylaşıma düştüğü. Bu dersin ölçtüğü şey budur.
- Yalnız bir platformda var olan özelliklerin payı. Bunlar her düzenekte tek kalır.
- Ekibin hangi kod tabanlarını bakımını sürdürebileceği. Bu bir ölçüm değil bir kapasite sorusudur.
- Uygulamanın platformun kendi görünümüne ve alışkanlıklarına ne kadar uyması gerektiği. Bu bir paylaşım sorusu değil bir ürün sorusudur.

İlk girdi ölçülür, ötekiler konuşulur. Hiçbir yaklaşım ilk girdiden tek başına seçilmez. Ölçüm seçime bir zemin verir ve sayıların nerede yanıltabileceğini düzyazı söyler.

#### Özet

- Bu dersin paylaşımı: tek, bir özellik için; dersin taşıyıcı ölçümü aynı altı özelliğin iki düzenekteki dağılımıdır. Ana ekran parçası yalnız bir platform için yazıldığından hangi düzenek seçilirse seçilsin tek kalır.
- Tek kod tabanı düzeneğinde altı özellik iki ortak, iki dallı, bir ikiz ve bir tek olarak dağıldı.
- Paylaşılan mantık ve yerel arayüz düzeneğinde aynı altı özellik iki ortak, sıfır dallı, üç ikiz ve bir tek olarak dağıldı; dallı iki özellik ikize döndü.
- Kural kodun yapısını sayar, bedelini sayamaz: hangi dağılımın daha az bakım isteyeceği özelliğin değişme sıklığına ve ekibin kapasitesine bağlıdır.
- Yaklaşım seçimi en az dört girdiye bakar: paylaşım dağılımı, yalnız bir platformda bulunan özellikler, ekibin bakım kapasitesi ve ürünün platformun görünümüne uyma ihtiyacı.

#### Sonraki Adım

Bu kurs yaklaşımların kodu nasıl paylaştırdığını ölçtü. Sıradaki kurs, `Platform Tasarım Dilleri Farkı`, paylaşımın sınırını başka bir yerden çizer: aynı kod iki platformda çalışsa bile kullanıcının beklediği görünüm ve davranış platforma göre ayrışabilir.

## Platform Gerçekleri

Platform tasarım dilleri arasındaki fark, yerel modül ihtiyacı, başarım beklentileri ve mağaza ile yayın kısıtları.

### Platform Tasarım Dilleri Farkı

Source: https://academia.sh/tr/kurslar/capraz-platform-yaklasimlari/platform-tasarim-dilleri-farki

İki platform aynı işi farklı bir tasarım diliyle sunar. Ders, gezinme kalıbını ekran türünden seçen tek işlevin iki platformda farklı sonuç vermesini dallı olarak sınıflar ve uyarlanmış tasarımın paylaşımı artırdığını ölçer.

Önceki konu yaklaşımları karşılaştırdı ve son derste seçim ölçütlerini sıraladı. Ölçütlerden biri, arayüzün iki platformda aynı mı görüneceği yoksa her platformun kendi diliyle mi görüneceğiydi. Bu konu o sorunun arkasındaki gerçekleri açar ve ilk gerçek tasarım dilidir. Kursun sorusu değişmez: **bu özelliğin kodu platformlar arasında nasıl paylaşılıyor?** Yanıtın dört biçimi ve çekirdeğin adları açılış dersinde tanıtıldı; burada yeniden tanımlanmaz, atıf yapılır.

Kısaca hatırlayalım. Aynı kod her örnekte aynı sonucu veriyorsa özellik **ortak**tır. Aynı kod en az bir örnekte platforma göre farklı sonuç veriyorsa **dallı**dır. Özellik iki ayrı işlev nesnesiyle yazılmışsa, sonuçlar aynı çıksa bile **ikiz**dir. Bir platformda hiç yoksa **tek**tir. Üst ad paylaşımdır; bir özelliğin paylaşımı denince bu dört yanıttan biri anlaşılır.

Kuralı çekirdek verir ve her derste aynı dosyayla gelir. Paylaşımı etiket değil kural belirler; kural elle atanmaz.

```javascript
// 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}`);
  }
}
```

#### Tasarım Dili Neyi Ayırır

Tasarım dili (design language), bir platformun arayüze koyduğu ortak görünüş ve davranış kurallarıdır. Kullanıcı bir platformda ekranların nasıl açıldığını, nasıl kapandığını ve ana gezintinin nerede durduğunu öğrenmiştir. Başka bir platformda aynı işin farklı yapılması, yanlış değil alışılmamış görünür.

Fark üç yerde belirir. Birincisi gezinme kalıbıdır (navigation pattern): ana bölümler arasında geçiş alt sekmeyle, yan çekmeceyle ya da ekran yığınıyla yapılabilir ve her platformun alışkanlığı farklıdır. İkincisi geri davranışıdır: bir platformda ekranın kenarından kaydırmak geri götürür, öbüründe sistemin geri hareketi ya da düğmesi vardır. Üçüncüsü sekme çubuğu ile araç çubuğunun yeridir: aynı sekmeler bir platformda altta, öbüründe üstte durabilir.

Çapraz platform kod bu farkı ikiye bölerek karşılar. Ya tek bir işlev ekran türüne ve platforma bakıp kalıbı seçer, ya da her platform kendi işlevini yazar. İlki dallıdır, ikincisi ikizdir. İkisi de kullanıcıya platformuna uygun görünüş verebilir; fark kodun nasıl paylaşıldığındadır.

#### Tek İşlev, İki Kalıp

Model, ekran türünden gezinme kalıbını seçen tek bir işlevdir (`navigationStyle`). Örnekler ekran türleridir: ana ekran, liste, detay, ayarlar. İşlev iki platformda da aynı nesne olarak verilir, yani kod bir tanedir. Ana ekranda iki platform farklı kalıp seçer, ayarlar ekranında da öyle; liste ile detayda ikisi de yığını seçer.

```javascript
// main.mjs — ekran turunden gezinme kalibini secen tek islev
import { Sharing } from "./sharing.mjs";

const screens = ["ana", "liste", "detay", "ayarlar"];

function navigationStyle(screen, platform) {
  if (screen === "ana") return platform === "ios" ? "alt-sekme" : "alt-cubuk";
  if (screen === "ayarlar") return platform === "ios" ? "yigin" : "cekmece";
  return "yigin";
}

const nav = new Sharing("gezinme-kalibi", { ios: navigationStyle, android: navigationStyle }, screens);
nav.line();
```

```text
gezinme-kalibi: kod=1 | fark=2/4 -> dalli
```

Kod bir tane, ama dört ekranın ikisinde sonuç platforma göre değişiyor. Bu tanım gereği dallıdır. İşlev kendi içinde `platform` parametresine bakıyor; ortak kod ile platform farkı aynı yerde buluşuyor. Bir ekip bu yapıyı genellikle tek bir dosyada tutar ve platform farkını bir `if` içinde okunur bırakır.

Kural burada neyi görmüştür, neyi görmemiştir? Kodun bir olduğunu ve iki örnekte sonucun ayrıldığını görmüştür. İşlevin ne kadar büyüdüğünü görmez: iki dallı bir işlev ile yirmi dallı bir işlev aynı sınıfa girer. Bunu düzyazı söyler.

**PG1.** Model, bu paylaşımın kuralını kurar; model çerçevenin kendisi değildir. Modelde gezinme kalıbı bir dizgidir ve ekrana hiçbir şey çizilmez. Gerçek bir uygulamada aynı kararı çerçevenin gezinme düzeni verir ve platform kodu şuna benzer.

```swift
// Platform kodu: TabRoot
import SwiftUI

struct TabRoot: View {
    var body: some View {
        TabView {
            HomeScreen().tabItem { Label("Ana", systemImage: "house") }
            SettingsScreen().tabItem { Label("Ayarlar", systemImage: "gear") }
        }
    }
}
```

```kotlin
// Platform kodu: AppScaffold
@Composable
fun AppScaffold(navController: NavHostController) {
    Scaffold(bottomBar = { BottomBar(navController) }) { padding ->
        NavHost(navController, startDestination = "home", modifier = Modifier.padding(padding)) {
            composable("home") { HomeScreen() }
            composable("settings") { SettingsScreen() }
        }
    }
}
```

İki parça aynı ana ekranı iki ayrı yerel arayüz diliyle yazar. Parçalar çıktı vermez; ölçülen yalnız modeldeki seçimdir. Bu iki parçayı paylaşan ortak bir üst katman varsa, modeldeki `navigationStyle` gibi, kalıbı ona soran tek bir işlev olurdu.

#### Üç Özellik, Üç Cevap

Tek bir özellik tablo olmaz. Aynı ekran kümesinde üç özelliği birlikte soralım: geri davranışı, sekme yeri ve marka rengi. Geri davranışı ana ekranda ve diğerlerinde platforma göre ayrılır. Sekme yeri yalnız sekme taşıyan ekranlarda anlam kazanır. Marka rengi ise platformdan bağımsız bir sabittir.

```javascript
// main.mjs — uc ozellik, ayni dort ekran
import { Sharing, countSharing } from "./sharing.mjs";

const screens = ["ana", "liste", "detay", "ayarlar"];

function backBehavior(screen, platform) {
  if (screen === "ana") return platform === "ios" ? "yok" : "uygulamadan-cik";
  return platform === "ios" ? "kenar-kaydirma" : "sistem-geri";
}

function tabPlacement(screen, platform) {
  if (screen === "ana" || screen === "liste") return platform === "ios" ? "alt" : "ust";
  return "yok";
}

function brandColor() {
  return "mavi";
}

const rows = [
  new Sharing("geri-davranisi", { ios: backBehavior, android: backBehavior }, screens),
  new Sharing("sekme-yeri", { ios: tabPlacement, android: tabPlacement }, screens),
  new Sharing("marka-rengi", { ios: brandColor, android: brandColor }, screens),
];
rows.forEach((r) => r.line());
countSharing(rows);
```

```text
geri-davranisi: kod=1 | fark=4/4 -> dalli
sekme-yeri: kod=1 | fark=2/4 -> dalli
marka-rengi: kod=1 | fark=0/4 -> ortak
ortak: 1
dalli: 2
ikiz: 0
tek: 0
```

Geri davranışı dört ekranın dördünde ayrılır, sekme yeri ikisinde ayrılır. İkisi de tek işlevdir ve ikisi de dallıdır; dallı olmak için her ekranda ayrılmak gerekmez, tek bir örnekte ayrılmak yeter. Marka rengi hiçbir ekranda ayrılmaz ve ortaktır. Burada küçük bir kural da görünür: platformu hiç okumayan, sabit dönüş veren bir işlev her zaman ortak çıkar. Bunun nedeni işlevin iyi paylaşılmış olması değil, ayıracak bir şey taşımamasıdır.

#### Aynı Davranış, İki İşlev

Geri davranışını başka biçimde yazalım. Bu kez her platform kendi işlevine sahip: biri kenar kaydırmasını, öbürü sistemin geri hareketini bilir. Sonuçlar bir önceki bloktakiyle birebir aynıdır, ama özellik artık iki ayrı işlev nesnesidir.

İkinci özellik daha dikkat çekicidir: ekran başlığının biçimi. İki platformun işlevleri aynı sonucu verir, yine de ayrı yazılmışlardır. Kural sonucu değil nesne sayısını sayar, bu yüzden bu da ikizdir.

```javascript
// main.mjs — ayni davranis, iki ayri islev
import { Sharing } from "./sharing.mjs";

const screens = ["ana", "liste", "detay", "ayarlar"];

function iosBack(screen) {
  return screen === "ana" ? "yok" : "kenar-kaydirma";
}

function androidBack(screen) {
  return screen === "ana" ? "uygulamadan-cik" : "sistem-geri";
}

const titleIos = (screen) => screen.toUpperCase();
const titleAndroid = (screen) => screen.toUpperCase();

new Sharing("geri-davranisi-ikiz", { ios: iosBack, android: androidBack }, screens).line();
new Sharing("baslik-bicimi", { ios: titleIos, android: titleAndroid }, screens).line();
```

```text
geri-davranisi-ikiz: kod=2 | fark=4/4 -> ikiz
baslik-bicimi: kod=2 | fark=0/4 -> ikiz
```

Geri davranışı bu kez ikiz çıktı, ama sonuçlar önceki bloktakiyle aynı: dört ekranın dördünde ayrılıyor. Değişen yalnız kodun biçimidir. Başlık biçimi daha da açık bir örnek: iki işlev aynı sonucu veriyor, fark sıfır, yine de kod iki. Kural burada iki şeyi birlikte söyler: sonuç ortak, kod ikiz.

Bu ayrımın bedeli gerçektir. Dallı bir özellikte platform farkı tek yerde okunur; ikiz bir özellikte aynı farkı sürdürmek için iki işlevi birlikte güncellemek gerekir. Kural bu bedeli görmez: üç satırlık bir ikiz ile üç yüz satırlık bir ikiz aynı sınıfa girer. Büyüklüğü düzyazı söyler.

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

Tasarımı platformlara bırakmak yerine iki platformda aynı kalıba uyarlayalım. Ana ekranda alt sekme, diğer her yerde yığın: iki platform da aynı kararı verir. İşlev tek, sonuç her ekranda aynı.

```javascript
// main.mjs — iki platformda ayni kalibi veren uyarlanmis tasarim
import { Sharing, countSharing } from "./sharing.mjs";

const screens = ["ana", "liste", "detay", "ayarlar"];

function navigationStyle(screen, platform) {
  if (screen === "ana") return platform === "ios" ? "alt-sekme" : "alt-cubuk";
  if (screen === "ayarlar") return platform === "ios" ? "yigin" : "cekmece";
  return "yigin";
}

function adaptedNavigation(screen) {
  return screen === "ana" ? "alt-sekme" : "yigin";
}

const rows = [
  new Sharing("gezinme-kalibi-platforma-gore", { ios: navigationStyle, android: navigationStyle }, screens),
  new Sharing("gezinme-kalibi-uyarlanmis", { ios: adaptedNavigation, android: adaptedNavigation }, screens),
];
rows.forEach((r) => r.line());
countSharing(rows);
```

```text
gezinme-kalibi-platforma-gore: kod=1 | fark=2/4 -> dalli
gezinme-kalibi-uyarlanmis: kod=1 | fark=0/4 -> ortak
ortak: 1
dalli: 1
ikiz: 0
tek: 0
```

Aynı özellik ilk satırda dallıydı, ikinci satırda ortak oldu. Kod değişmedi, hâlâ tek işlev; değişen, işlevin platforma göre ayrılmayı bırakmasıdır. Paylaşım arttı: dallıdan ortağa geçildi.

Kuralın göremediği şey bu geçişin bedelidir. Ortak kalıp, platformlardan birinin tasarım dilinden sapar: ana ekranda alt sekme bir platformda alışkanlıkla örtüşürken öbüründe örtüşmeyebilir. Bu sapmayı model ölçmez ve ölçemez, çünkü kullanıcı alışkanlığı bir işlev çağrısının sonucu değildir. Ölçülen yalnız şudur: ortak kalıp iki platformda da aynı sonucu verir. Alışkanlıktan ne kadar uzaklaşıldığı düzyazıda söylenen ve ekibin kendisinin tarttığı bir karardır.

**PG2.** Model, bir kalıbın iki platformda aynı sonucu verdiğini ölçer, kullanıcının o kalıbı doğal bulup bulmadığını ölçmez. Bu varsayım tasarım kararı yerine geçmez. Ortak paylaşımın tasarım dilinden bir sapma içerdiğini bilmek, sapmanın kabul edilebilir olduğunu söylemez.

#### Özet

- Bu dersin paylaşımı: dallı. Gezinme kalıbını ekran türünden seçen tek işlev, dört ekranın ikisinde platforma göre farklı kalıp verdi.
- Dört ölçümde dallı, ikiz ve ortak çıktı: geri davranışı ve sekme yeri dallı, aynı geri davranışı iki işlevle yazılınca ikiz, marka rengi ortaktı.
- Platformu okumayan, sabit dönüş veren bir işlev her zaman ortak çıkar; bu işlevin iyi paylaşıldığını değil, ayıracak bir şey taşımadığını gösterir.
- Başlık biçimi iki işlevle aynı sonucu verse de ikiz çıktı: kural sonucu değil kodun biçimini sayar.
- Tasarımı iki platformda aynı kalıba uyarlamak paylaşımı dallıdan ortağa çıkarır; tasarım dilinden sapmayı kural değil düzyazı söyler.

#### Sonraki Adım

Gezinme kalıbı ortak koddan seçilebildi, çünkü seçim yalnız bir dizgiydi. Platformun kendi yeteneğine dokunan bir iş böyle çözülmez: biyometrik doğrulama isteğini ortak kod tek başına yapamaz. Sıradaki ders, `Yerel Modül İhtiyacı`, böyle bir isteği yapan kodun platformlar arasında nasıl paylaşıldığını ölçer.

### Yerel Modül İhtiyacı

Source: https://academia.sh/tr/kurslar/capraz-platform-yaklasimlari/yerel-modul-ihtiyaci

Ortak kod her platform yeteneğine ulaşamaz. Ders, biyometrik doğrulama isteğini her platformda ayrı yazılan bir yerel modül olarak ikiz diye sınıflar ve yalnız bir platformda bulunan yeteneğin tek çıktığını ölçer.

Önceki ders gezinme kalıbını ortak koddan seçmenin mümkün olduğunu gösterdi, çünkü seçim yalnız bir dizgiydi. Aynı yol platformun kendi yeteneğine dokunan bir işte kapanır: kullanıcının yüzünü ya da parmak izini okuyan donanım ortak koddan çağrılamaz. Ortak kod, platformun sunduğu yeteneği ancak o platformda yazılmış bir parça aracılığıyla kullanır. Kursun sorusu aynı kalır: **bu özelliğin kodu platformlar arasında nasıl paylaşılıyor?** Dört yanıtın tanımı açılış dersindeki gibidir ve burada yeniden yazılmaz.

Kuralı çekirdek verir ve her derste aynı dosyayla gelir.

```javascript
// 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}`);
  }
}
```

#### Yerel Modül Nedir

Yerel modül (native module), platformun kendi dilinde yazılmış ve ortak koddan çağrılan küçük bir parçadır. Biyometrik doğrulamada bu parça, platformun doğrulama arayüzünü açar ve sonucu ortak koda geri döndürür. Ortak kod modülün içini bilmez; yalnız adını ve aldığı değerleri bilir. Bu çağrı yoluna platform kanalı (platform channel) denir: ortak koddan platforma bir ad ve bir bağımsız değişken gider, platformdan bir yanıt gelir.

Modülün yazılması gerekir, çünkü doğrulama penceresini ortak kod çizemez. Pencerenin biçimi, kayıtlı yüz ya da parmak izinin okunuşu, kullanıcı vazgeçtiğinde dönen durum, hepsi platformun işidir. İki platformun arayüzleri farklı olduğu için ortaya iki ayrı parça çıkar.

**PG1.** Model, bu paylaşımın kuralını kurar; model çerçevenin kendisi değildir. Modelde biyometrik doğrulama, kayıt durumunu alıp bir istek dizgisi döndüren işlevdir; ekranda hiçbir pencere açılmaz. Gerçek modül ise her platformda şuna benzer iki parçadır.

```swift
// Platform kodu: IosBiometricModule
import LocalAuthentication

func requestBiometric(reply: @escaping (Bool) -> Void) {
    let context = LAContext()
    var error: NSError?
    guard context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) else {
        reply(false)
        return
    }
    context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics,
                           localizedReason: "Hesabini ac") { success, _ in
        reply(success)
    }
}
```

```kotlin
// Platform kodu: AndroidBiometricModule
fun requestBiometric(activity: FragmentActivity, reply: (Boolean) -> Unit) {
    val executor = ContextCompat.getMainExecutor(activity)
    val callback = object : BiometricPrompt.AuthenticationCallback() {
        override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) = reply(true)
        override fun onAuthenticationError(code: Int, message: CharSequence) = reply(false)
    }
    val info = BiometricPrompt.PromptInfo.Builder()
        .setTitle("Hesabini ac")
        .setNegativeButtonText("Vazgec")
        .build()
    BiometricPrompt(activity, executor, callback).authenticate(info)
}
```

İki parça aynı adı taşır ve aynı işi yapar, ama aralarında ortak tek satır yoktur: biri bir bağlam nesnesi ve politika ister, öbürü bir `activity` nesnesi, bir yürütücü ve bir geri çağrı nesnesi. Parçalar çıktı vermez; ölçülen yalnız modeldeki iki işlevdir.

Bu soru, M31/K02'de sorduğumuz soruya benzer ama aynı değildir. Orada bir isteğin çalışma anındaki sonucu sınıflanıyordu: tam, ikame, bekleyen ya da karşılıksız. Burada sonuç sınıflanmaz; isteği yapan kodun platformlar arasındaki dağılımı sınıflanır. Aynı istek bir platformda tam, öbüründe karşılıksız sonuçlansa bile kod yine ikiz olabilir.

#### İki Platform, İki Modül

Model, her platformun kayıt durumuna göre ne yaptığını gösteren iki ayrı işlevdir: `iosBiometric` ve `androidBiometric`. Örnekler dört kayıt durumudur: parmak izi kayıtlı, yüz kayıtlı, hiç kayıt yok, kilitli. İki işlev ayrı nesnelerdir; kural bunu kod sayısı olarak görür.

```javascript
// main.mjs — biyometrik dogrulama istegi her platformda ayri yazilir
import { Sharing } from "./sharing.mjs";

const states = ["parmak-izi", "yuz", "kayit-yok", "kilitli"];

function iosBiometric(state) {
  if (state === "kayit-yok") return "parola-yedegi";
  if (state === "kilitli") return "bekle";
  return "yerel-onay-penceresi";
}

function androidBiometric(state) {
  if (state === "kayit-yok") return "ayara-yonlendir";
  if (state === "kilitli") return "bekle";
  return "sistem-onay-penceresi";
}

new Sharing("biyometrik-istek", { ios: iosBiometric, android: androidBiometric }, states).line();
```

```text
biyometrik-istek: kod=2 | fark=3/4 -> ikiz
```

Kod iki, ve dört durumun üçünde iki platformun isteği ayrılıyor. Kilitli durumda iki modül de bekliyor; bu örnekte sonuç aynı çıksa da özellik ikizdir, çünkü kural önce kod sayısına bakar. Burada dallı çıkması mümkün değildir: iki ayrı işlev verildiğinde sonuç ne olursa olsun kural ikiz der.

Bir modül yazmanın bedeli iki yerde ödenir. Her yeni durum, örneğin ikinci bir doğrulama türü, iki modüle ayrı ayrı eklenir. Her hata düzeltmesi de iki modülde aranır. Kural bu bedeli görmez: üç satırlık bir ikiz ile üç yüz satırlık bir ikiz aynı sınıfa girer. Bedelin büyüklüğünü düzyazı söyler.

#### Çağrı Ortak, Modül İkiz

Aynı özelliğin iki parçası vardır ve ikisi aynı biçimde paylaşılmaz. Çağrıyı yapan taraf ortak kodda durur: kanal adını ve bağımsız değişkeni hazırlar, çağrıyı gönderir. Çağrının karşılandığı taraf yerel modüldür. Çağrı iki platformda da aynıdır, çünkü ortak koddur; karşılayan taraf aynı değildir.

```javascript
// main.mjs — ortak koddan yerel module giden cagri
import { Sharing, countSharing } from "./sharing.mjs";

const states = ["parmak-izi", "yuz", "kayit-yok", "kilitli"];

function invokeModule(state) {
  return { channel: "biyometrik", argument: state };
}

function channelName() {
  return "biyometrik";
}

const rows = [
  new Sharing("kanal-cagrisi", { ios: invokeModule, android: invokeModule }, states),
  new Sharing("kanal-adi", { ios: channelName, android: channelName }, states),
];
rows.forEach((r) => r.line());
countSharing(rows);
```

```text
kanal-cagrisi: kod=1 | fark=0/4 -> ortak
kanal-adi: kod=1 | fark=0/4 -> ortak
ortak: 2
dalli: 0
ikiz: 0
tek: 0
```

İki satırda da kod bir ve fark sıfır; ikisi de ortaktır. `channelName` sabit dönüş verdiği için ortak olmaya mahkûmdur. `invokeModule` ise bir argümanı sarıp ileten gerçek bir işlevdir ve yine de iki platformda aynı sonucu verir, çünkü platform farkı çağrının içinde değil, çağrının karşılığındadır. Ortak kodun çağrısı ortak kalır; özelliğin geri kalanı kalmaz.

#### Aynı Özellik, İki Ölçüm Düzeyi

Bir özelliğin paylaşımı, kurala hangi işlev nesnelerini verdiğinize bağlıdır. Çağıran tarafta platforma göre doğru modülü seçen tek bir sarmalayıcı yazalım. Bu sarmalayıcıyı iki platform için de aynı nesne olarak verirsek kural kod sayısını bir görür. Modüllerin kendisini verirsek iki görür.

```javascript
// main.mjs — ayni ozellik, iki olcum duzeyi
import { Sharing, countSharing } from "./sharing.mjs";

const states = ["parmak-izi", "yuz", "kayit-yok", "kilitli"];

function iosBiometric(state) {
  if (state === "kayit-yok") return "parola-yedegi";
  if (state === "kilitli") return "bekle";
  return "yerel-onay-penceresi";
}

function androidBiometric(state) {
  if (state === "kayit-yok") return "ayara-yonlendir";
  if (state === "kilitli") return "bekle";
  return "sistem-onay-penceresi";
}

function authenticate(state, platform) {
  return platform === "ios" ? iosBiometric(state) : androidBiometric(state);
}

const rows = [
  new Sharing("yerel-modul", { ios: iosBiometric, android: androidBiometric }, states),
  new Sharing("dogrulama-sarmalayici", { ios: authenticate, android: authenticate }, states),
];
rows.forEach((r) => r.line());
countSharing(rows);
```

```text
yerel-modul: kod=2 | fark=3/4 -> ikiz
dogrulama-sarmalayici: kod=1 | fark=3/4 -> dalli
ortak: 0
dalli: 1
ikiz: 1
tek: 0
```

Modüller ikiz, sarmalayıcı dallı. İkisi aynı özelliğin iki yüzüdür ve kural ikisine iki ayrı yanıt verir.

**PG2.** Model, kuralın hangi işlevi gördüğünü ölçer; ekibin özelliği hangi sınırda kestiğini ölçmez. Bu derste özelliğin kodu denince yerel modül anlaşılır, çünkü ihtiyaç duyulan şey odur. Sarmalayıcıyı ölçmek yanlış değildir ama başka bir soruya yanıt verir: seçimin tek yerde yapılıp yapılmadığına.

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

Şimdi bir yeteneğin yalnız bir platformda bulunduğunu varsayalım. Uygulama arka plana alınırken ekranı bulanıklaştıran bir gizlilik yeteneği olsun. Yeteneğin yalnız iOS tarafında bulunduğunu kabul ediyoruz; Android tarafında karşılığı yok. Modül yalnız tek tarafta yazılır. Ortak koddan modüle giden çağrı ise iki platformda da aynı kalır.

**PG3.** Burada varsayım, yeteneğin yalnız bir platformda bulunmasıdır; model bunu platformun gerçek durumu olarak sunmaz, bir ihtiyaç örneği olarak kurar. Gerçek bir yeteneğin hangi platformlarda bulunduğu platform belgesinden doğrulanmadan bir ders ya da ürün kararına dayanak yapılmaz.

```javascript
// main.mjs — yalniz bir platformda bulunan yetenek
import { Sharing, countSharing } from "./sharing.mjs";

const states = ["on-plan", "arka-plan", "kilitli"];

function invokeModule(state) {
  return { channel: "gizlilik", argument: state };
}

function iosPrivacyScreen(state) {
  return state === "arka-plan" ? "bulaniklastir" : "goster";
}

const rows = [
  new Sharing("kanal-cagrisi", { ios: invokeModule, android: invokeModule }, states),
  new Sharing("gizlilik-ekrani", { ios: iosPrivacyScreen }, states),
];
rows.forEach((r) => r.line());
countSharing(rows);
```

```text
kanal-cagrisi: kod=1 | fark=0/3 -> ortak
gizlilik-ekrani: kod=1 | eksik=android -> tek
ortak: 1
dalli: 0
ikiz: 0
tek: 1
```

Çağrı ortak, özellik tek. Kural eksik tarafı ayrıca yazar: `eksik=android`. Ortak kodun çağrısı iki platformda da çalışsa da çağrının karşılığı bir platformda yoktur ve özelliğin kodu orada yazılmamıştır. Bu bir hata değildir; yalnız bir platformun sunduğu bir yetenekte ortak kodun yapabileceği en fazla şey, o platformda çağrıyı sessizce karşılamamak ya da bir yedek yazmaktır.

Tek, ikiz kadar bedelli olmayabilir. İkizde iki modülün birlikte yaşatılması gerekir; tekte bakılacak tek modül vardır. Ama tek, özelliğin iki platformda aynı görünmediğini de söyler. Kural bu eşitsizliğin kullanıcıya etkisini görmez; bunu ekip tartar.

#### Özet

- Bu dersin paylaşımı: ikiz. Biyometrik doğrulama isteği her platformda ayrı bir yerel modül olarak yazıldı ve kural iki kod saydı.
- Ortak koddan modüle giden çağrı ortak çıktı; çağrı ortak kalsa da modül ikiz kaldı.
- Aynı özellik, modüller verilince ikiz, onları seçen sarmalayıcı verilince dallı çıktı: kural hangi işlevi gördüğünü ölçer.
- Yalnız bir platformda bulunan yeteneğin modülü tek çıktı; ortak kodun çağrısı ortak olsa da özelliğin kodu bir platformda yoktur.
- Kural kod sayısını görür, modülün büyüklüğünü ve bakım bedelini görmez; bunları düzyazı söyler.

#### Sonraki Adım

Bu derste yerel modül bir doğrulama penceresini açtı ve iş birimi olarak sayılacak bir şey üretmedi. Sıradaki ders, `Başarım Beklentileri`, yerel kodun bir başka nedenini ele alır: sık çalışan bir yolu ortak koddan çıkarıp yerel kod olarak yazmanın, ortak koddan her geçişte neye mal olduğunu.

### Başarım Beklentileri

Source: https://academia.sh/tr/kurslar/capraz-platform-yaklasimlari/basarim-beklentileri

Sık çalışan bir yolu ortak koddan çıkarıp yerel kodla yazmak başarım için yapılır. Ders, küçük resim çözmeyi her platformda ayrı yazılan ikiz olarak sınıflar ve köprü geçişlerini iş birimi olarak sayar.

Önceki ders ortak kodun platform yeteneğine tek başına ulaşamadığını ve bunun için ayrı bir yerel modül gerektiğini gösterdi. Yerel kodun bir nedeni daha vardır, ve bu kez ortak kod yeteneğe ulaşabilir ama bunu yeterince hızlı yapamaz. Kursun sorusu aynı kalır: **bu özelliğin kodu platformlar arasında nasıl paylaşılıyor?** Dört yanıtın tanımı açılış dersindeki gibidir ve burada yeniden yazılmaz.

Kuralı çekirdek verir ve her derste aynı dosyayla gelir.

```javascript
// 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}`);
  }
}
```

#### Sıcak Yol ve Köprü Geçişi

Sıcak yol (hot path), uygulamanın sık sık çalışan ve kullanıcının beklediği an ile doğrudan ilgili kod yoludur. Bir listeyi kaydırırken her satır için çalışan işlem tipik bir sıcak yoldur: bir kare (frame) çizilirken bu işlem yeterince çabuk bitmezse kullanıcı takılma görür. Ekip böyle bir yolu yerel koda taşıyabilir, çünkü yerel kod platformun kendi çözücüsüne doğrudan erişir.

Ortak kod ile yerel kod arasındaki sınıra köprü (bridge) diyoruz. Ortak koddan yerel koda yapılan her çağrı bir köprü geçişidir (bridge crossing). Geçişin kendisinin de bir işi vardır: değerler sınırdan taşınır, çağrı sıraya girer, yanıt geri gelir. Bu işin gerçek büyüklüğü çerçeveye ve platforma göre değişir; ders bunu ölçmez ve bir sayı yazmaz. Başarım bu derste iş birimiyle ölçülür: bir ekranda kaç köprü geçişi yapıldığı.

Bu yüzden iki soru ayrı tutulmalıdır. Birincisi başarımdır: hızlı yol için kaç geçiş gerekiyor? İkincisi paylaşımdır: hızlı yol iki platformda ayrı mı yazılmış? Kursun ölçüsü ikincisidir. Birincisi paylaşımı etkileyen bir gerekçedir ama paylaşımı veren değildir.

Köprü geçişlerini saymak için küçük bir yardımcı dosya kullanacağız. `Sharing` çekirdeğine dokunmaz; yalnız ortak koddan çağrılan her işlevin kaç kez çağrıldığını tutar.

```javascript
// bridge.mjs — ortak koddan platforma yapilan her gecisi sayar
export function createBridge() {
  let crossings = 0;
  return {
    cross(fn, ...args) {
      crossings += 1;
      return fn(...args);
    },
    count() {
      return crossings;
    },
  };
}
```

**PG1.** Model, bu paylaşımın kuralını kurar; model çerçevenin kendisi değildir. Modelde bir köprü geçişi, `cross` çağrısının bir kez yapılmasıdır; sınırın gerçek maliyeti modellenmez. Gerçek bir uygulamada küçük resim çözme her platformda kendi çözücüsünü çağırır ve parçalar şuna benzer.

```swift
// Platform kodu: IosThumbnailDecoder
import ImageIO

func decodeThumbnail(data: Data, maxSize: Int) -> CGImage? {
    guard let source = CGImageSourceCreateWithData(data as CFData, nil) else { return nil }
    let options: [CFString: Any] = [
        kCGImageSourceCreateThumbnailFromImageAlways: true,
        kCGImageSourceThumbnailMaxPixelSize: maxSize
    ]
    return CGImageSourceCreateThumbnailAtIndex(source, 0, options as CFDictionary)
}
```

```kotlin
// Platform kodu: AndroidThumbnailDecoder
fun decodeThumbnail(data: ByteArray, maxSize: Int): Bitmap? {
    val bounds = BitmapFactory.Options().apply { inJustDecodeBounds = true }
    BitmapFactory.decodeByteArray(data, 0, data.size, bounds)
    val options = BitmapFactory.Options().apply {
        inSampleSize = maxOf(1, maxOf(bounds.outWidth, bounds.outHeight) / maxSize)
    }
    return BitmapFactory.decodeByteArray(data, 0, data.size, options)
}
```

İki parça aynı işi yapar ama platformun kendi görüntü çözücüsünü kullanır ve ortak satır içermez. Parçalar çıktı vermez; ölçülen modeldeki iki işlevdir.

#### Liste Öğesi Başına Bir Geçiş

Altı öğeli bir liste olsun. Her öğe üç boyuttan birinde bir küçük resim ister. Küçük resim çözme her platformda ayrı yazılmış: biri iOS tarafının, öbürü Android tarafının işlevi. Ortak kod listeyi dolaşır ve her öğe için yerel işlevi ayrı ayrı çağırır.

```javascript
// main.mjs — kucuk resim cozme her platformda ayri, liste ogesi basina bir gecis
import { Sharing } from "./sharing.mjs";
import { createBridge } from "./bridge.mjs";

const sizes = ["kucuk", "orta", "buyuk"];
const list = ["kucuk", "kucuk", "orta", "kucuk", "buyuk", "kucuk"];

function iosDecodeThumbnail(size) {
  return `ios-cozuldu-${size}`;
}

function androidDecodeThumbnail(size) {
  return `android-cozuldu-${size}`;
}

const decodeThumbnail = { ios: iosDecodeThumbnail, android: androidDecodeThumbnail };
const bridge = createBridge();

for (const size of list) bridge.cross(decodeThumbnail.ios, size);

new Sharing("kucuk-resim-cozme", decodeThumbnail, sizes).line();
console.log(`kopru-gecisi: ${bridge.count()}`);
```

```text
kucuk-resim-cozme: kod=2 | fark=3/3 -> ikiz
kopru-gecisi: 6
```

Kod iki, ve üç boyutun üçünde iki platformun çıktısı ayrılıyor: ikiz. Altı öğe için altı köprü geçişi yapıldı. Sayım tek bir platformun yolunu izler, çünkü uygulama aynı anda yalnız bir platformda çalışır.

Bu sonuç iki şey söyler. Birincisi paylaşımdır: hızlı yol iki platformda ayrı yazıldığı için özellik ikizdir. İkincisi iş birimidir: liste uzadıkça geçiş sayısı onunla birlikte artar. İkisi bağımsızdır.

#### Toplu Çağrı

Geçişleri azaltmanın bilinen yolu çağrıyı toplamaktır: ortak kod altı küçük resmi tek bir istekte yerel koda verir, yerel kod hepsini çözüp tek bir yanıtla döner. Yerel işlevler aynıdır, yalnız çağrı biçimi değişir.

```javascript
// main.mjs — ayni yerel islevler, listenin tamami tek gecista
import { Sharing } from "./sharing.mjs";
import { createBridge } from "./bridge.mjs";

const sizes = ["kucuk", "orta", "buyuk"];
const list = ["kucuk", "kucuk", "orta", "kucuk", "buyuk", "kucuk"];

function iosDecodeThumbnail(size) {
  return `ios-cozuldu-${size}`;
}

function androidDecodeThumbnail(size) {
  return `android-cozuldu-${size}`;
}

const decodeThumbnail = { ios: iosDecodeThumbnail, android: androidDecodeThumbnail };
const bridge = createBridge();

bridge.cross((batch) => batch.map(decodeThumbnail.ios), list);

new Sharing("kucuk-resim-cozme-toplu", decodeThumbnail, sizes).line();
console.log(`kopru-gecisi: ${bridge.count()}`);
```

```text
kucuk-resim-cozme-toplu: kod=2 | fark=3/3 -> ikiz
kopru-gecisi: 1
```

Paylaşım aynı kaldı: kod iki, üçte üç fark, ikiz. Geçiş altıdan bire indi. İki ölçüm aynı kuralı, aynı işlevlerle sınadı ve yalnız iş birimi değişti. Bu, derse ayrım cümlesini kurma olanağı verir.

Bu soru, M31/K06'da sorduğumuz soruya benzer ama aynı değildir. Orada bir iş birimi ölçülüyordu: bir ölçümün bütçesini kaç örnekte aştığı. Burada da bir iş birimi, köprü geçişi, sayılır. Ama paylaşımı sayı vermez; paylaşımı, hızlı yolun iki platformda ayrı yazılıp yazılmadığı verir. İş birimi bir gerekçedir ve bir bedeli gösterir, yanıtı vermez.

**PG2.** Model, yerel kodun başarım gerekçesini ölçmez. Yerel yolun ortak yoldan gerçekten hızlı olup olmadığı, bu derste ölçülmediği için söylenmez; yalnız yerel yolun köprüde kaç geçiş yaptığı sayılır. Bir ekibin hızlı yolu yerel kodla yazma kararı, ekibin kendi ölçümüne dayanmalıdır.

Kural bu derste de iki şeyi görmez. Büyüklüğü görmez: üç satırlık bir çözücü ile üç yüz satırlık bir çözücü aynı sınıfa girer. Bedeli de görmez: iki platformda ayrı yazılmış bir çözücünün iki kez sınanması, iki kez düzeltilmesi ve ikisinin birbirinden ayrılması kuralın dışında kalır. Kural yapıyı görür; bedeli düzyazı söyler.

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

Aynı hesabı bu kez ortak kodda, tek bir işlevle yapalım. Küçük resim çözme ortak kodda yazılsın. Çözülen her küçük resim çizim için platforma geçsin: yani ortak kod altı resmi çözer, altısını da ekrana ayrı ayrı gönderir.

```javascript
// main.mjs — ayni hesap ortak kodda tek islevle
import { Sharing, countSharing } from "./sharing.mjs";
import { createBridge } from "./bridge.mjs";

const sizes = ["kucuk", "orta", "buyuk"];
const list = ["kucuk", "kucuk", "orta", "kucuk", "buyuk", "kucuk"];

function iosDecodeThumbnail(size) {
  return `ios-cozuldu-${size}`;
}

function androidDecodeThumbnail(size) {
  return `android-cozuldu-${size}`;
}

function decodeShared(size) {
  return `cozuldu-${size}`;
}

function drawOnScreen(picture) {
  return picture;
}

const platformBridge = createBridge();
platformBridge.cross((batch) => batch.map(iosDecodeThumbnail), list);
const platformRow = new Sharing(
  "kucuk-resim-cozme-yerel",
  { ios: iosDecodeThumbnail, android: androidDecodeThumbnail },
  sizes,
);

const sharedBridge = createBridge();
for (const size of list) sharedBridge.cross(drawOnScreen, decodeShared(size));
const sharedRow = new Sharing(
  "kucuk-resim-cozme-ortak",
  { ios: decodeShared, android: decodeShared },
  sizes,
);

platformRow.line();
console.log(`kopru-gecisi: ${platformBridge.count()}`);
sharedRow.line();
console.log(`kopru-gecisi: ${sharedBridge.count()}`);
countSharing([platformRow, sharedRow]);
```

```text
kucuk-resim-cozme-yerel: kod=2 | fark=3/3 -> ikiz
kopru-gecisi: 1
kucuk-resim-cozme-ortak: kod=1 | fark=0/3 -> ortak
kopru-gecisi: 6
ortak: 1
dalli: 0
ikiz: 1
tek: 0
```

Aynı hesap bu kez ortak çıktı: kod bir, fark sıfır. Paylaşım ikizden ortağa çıktı. Ama köprü geçişi birden altıya çıktı: ortak kod resmi kendisi çözdüğü için, çözülen her resim çizim için platforma ayrı gidiyor.

Kural burada yapıyı görmüştür ve bedeli görmemiştir. Paylaşımın artması, ekibin daha az şey yazdığını söyler; başarımın bozulduğunu ya da bozulmadığını söylemez. Altı geçişin gerçek bir ekranda neye mal olduğu, bu modelde ölçülmez.

**PG3.** Bu bloktaki geçiş sayıları, modelin kurduğu iki yolu karşılaştırır. Çözülmüş her resmin çizim için ayrı geçmesi bir varsayımdır; gerçek bir çerçeve çizim isteklerini birleştiriyor olabilir. Sayılar yalnız bu modeldeki yolları ayırır, bir çerçeveyi değerlendirmez.

#### Özet

- Bu dersin paylaşımı: ikiz. Küçük resim çözme her platformda yerel kodla ayrı yazıldığı için kural iki kod saydı.
- Köprü geçişi iş birimi olarak sayıldı: liste öğesi başına çağrıda altı, toplu çağrıda bir; paylaşım ikiz kaldı.
- Paylaşımı geçiş sayısı değil, hızlı yolun iki platformda ayrı yazılıp yazılmadığı verir; iş birimi gerekçeyi ve bedeli gösterir.
- Aynı hesap ortak kodda tek işlevle yazılınca paylaşım ortağa çıktı, modeldeki köprü geçişi birden altıya çıktı.
- Kural yapıyı görür, bedeli görmez; yerel yolun gerçekten hızlı olup olmadığı bu derste ölçülmedi ve söylenmedi.

#### Sonraki Adım

Bu konuda tasarım dili, yerel modül ve başarım, kodun platformlar arasında nasıl bölündüğünü gösterdi. Hepsi uygulamanın çalışma anıyla ilgiliydi. Uygulamanın kullanıcıya ulaşması ise ayrı bir sınırdan geçer. Sıradaki ders, `Mağaza ve Yayın Kısıtları`, uygulama mağazasının yayın kurallarını, mağaza incelemesini ve uygulama içi güncellemeyi çapraz platform kodu açısından ele alır.

### Mağaza ve Yayın Kısıtları

Source: https://academia.sh/tr/kurslar/capraz-platform-yaklasimlari/magaza-ve-yayin-kisitlari

Uygulama mağazası kuralları, mağaza incelemesi ve uygulama içi güncelleme kodun paylaşımını da belirler. Ders, güncelleme akışını yalnız bir platformda yazılan tek olarak sınıflar ve bayrakla kapatılan kodun dallı sayıldığını ölçer.

Önceki ders başarım gerekçesinin kodu ikize çevirebildiğini gösterdi. Bunların hepsi uygulamanın çalışma anıyla ilgiliydi. Kodun kullanıcıya ulaşması ise ayrı bir sınırdan geçer: uygulama mağazası. Kursun sorusu burada da aynı kalır: **bu özelliğin kodu platformlar arasında nasıl paylaşılıyor?** Dört yanıtın tanımı açılış dersindeki gibidir ve burada yeniden yazılmaz.

Kuralı çekirdek verir ve her derste aynı dosyayla gelir.

```javascript
// 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}`);
  }
}
```

#### Mağaza Kuralları ve Mağaza İncelemesi

Her platformun kendi uygulama mağazası, kendi yayın konsolu ve kendi yayın kuralları vardır. Bir uygulama yayımlanmadan önce mağaza incelemesinden geçer: paket, mağazanın o platform için koyduğu kurallara göre değerlendirilir. Çapraz platform kod tabanı tek olsa da bu hat tek değildir. Aynı düzeltme iki paketle iki ayrı mağaza hattından geçer ve kullanıcıya iki ayrı anda ulaşabilir.

Bu, paylaşım sorusunu iki yönden etkiler. Birincisi, kuralların kendisi platforma özgüdür: bir mağazanın izin verdiği bir davranış öbüründe kabul edilmeyebilir, ve bunu ortak kod bilemez. İkincisi, mağazanın sağladığı özellikler platforma özgüdür: bir platform kullanıcıya mağaza dışında bir güncelleme akışı sunabilir, öbürü sunmayabilir. İlk yön kodun hangi davranışı taşıyabileceğini sınırlar; ikinci yön ise kodun hangi platformda yazılabileceğini sınırlar. İki yön de paylaşım sınıfına yansır, ve bu ders ikincisini ölçer.

Her platformun paketi kendi sürüm kimliğini de taşır. Ortak koddan bağımsız olarak, platform tarafında şuna benzer parçalar bulunur.

```kotlin
// Platform kodu: AndroidVersionIdentity
android {
    defaultConfig {
        versionCode = 14
        versionName = "2.1"
    }
}
```

```swift
// Platform kodu: IosVersionIdentity
import Foundation

func installedVersion() -> String? {
    Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String
}
```

İki parça sürümü iki ayrı yerde, iki ayrı biçimde tutar. Parçalar çıktı vermez; yalnız sürüm bilgisinin ortak kodun dışında, platform tarafında durduğunu gösterir.

**PG1.** Model, bu paylaşımın kuralını kurar; model çerçevenin kendisi değildir. Modelde güncelleme kararı ve güncelleme akışı iki ayrı işlevdir. Sürüm numaraları ve durum adları örnektir; gerçek bir uygulamanın sürüm şemasını göstermez.

#### Güncelleme Kararı Ortaktır

Uygulama içi güncelleme (in-app update), kullanıcıyı mağazaya götürmeden yeni sürümün kurulmasını öneren akıştır. Akışın iki parçası vardır. Birincisi karardır: kurulu sürüm, sunucunun istediği en düşük sürümün altında mı? İkincisi akışın kendisidir: kullanıcıya teklifi göstermek, indirmeyi başlatmak, kurulumu tamamlamak. Karar saf bir karşılaştırmadır. Akış ise platformun mağaza altyapısına dayanır.

Önce kararı ölçelim. Üç örnek var: kurulu sürüm en düşük sürümün altında, eşit, üstünde.

```javascript
// main.mjs — guncelleme kararini veren islev iki platformda ayni
import { Sharing } from "./sharing.mjs";

const versions = [
  { installed: 12, minimum: 14 },
  { installed: 14, minimum: 14 },
  { installed: 15, minimum: 14 },
];

function isBelowMinimum(version) {
  return version.installed < version.minimum;
}

new Sharing(
  "guncelleme-karari",
  { ios: isBelowMinimum, android: isBelowMinimum },
  versions,
).line();
```

```text
guncelleme-karari: kod=1 | fark=0/3 -> ortak
```

Karar ortaktır: aynı işlev, üç örnekte de iki platformda aynı sonuç. Sürüm kimliği platform tarafında durur ama karşılaştırma bir sayı ile bir sayı arasındadır, ve bu ortak koda taşınabilir.

#### Akış Yalnız Bir Platformda

Şimdi akışı ölçelim. Bu modelde uygulama içi güncelleme akışının yalnız Android tarafında yazıldığını varsayıyoruz. iOS tarafında akışın karşılığı yok: kullanıcı mağazaya kendisi gider.

```javascript
// main.mjs — uygulama ici guncelleme akisi yalniz bir platformda yazilmis
import { Sharing } from "./sharing.mjs";

const states = ["surum-eski", "surum-guncel", "surum-zorunlu"];

function androidUpdate(state) {
  if (state === "surum-guncel") return "islem-yok";
  return state === "surum-zorunlu" ? "kurulum-baslatildi" : "guncelleme-teklif-edildi";
}

const inAppUpdate = { android: androidUpdate };

new Sharing("uygulama-ici-guncelleme", inAppUpdate, states).line();
```

```text
uygulama-ici-guncelleme: kod=1 | eksik=ios -> tek
```

`eksik=ios` çıktısı, iOS anahtarının tabloda hiç bulunmadığı anlamına gelir. Kural bir işlev bulamadığı için fark hesaplamaz; `fark` yerine `eksik` basılır ve sınıf tek olur. Kod bir, çünkü tek bir platformda tek bir işlev var.

**PG2.** Güncelleme akışının hangi platformda yazılabileceği bu derste bir varsayımdır. Modelde Android tarafı vardır, iOS tarafı yoktur. Bir platformun mağaza altyapısının gerçekte neyi sunduğu, derste ölçülmez ve bir platform adına bir iddia olarak yazılmaz; yalnız tablonun biçimi gösterilir.

Burada bir ayrım yapmak gerekir, çünkü iki soru birbirine karışır. Bu kod üretime çıkıyor mu? M12/K05'te sorduğumuz bu sorunun cevabı evet: Android paketine girer ve kullanıcıya ulaşır. Ama tek, kodun pakete girmemesi değil, bir platformda hiç yazılmamış olmasıdır. Pakete girmek ile yazılmış olmak ayrı sorulardır: ilki paketin içeriğini, ikincisi iki platformdaki kodun varlığını sorar.

Karar ortak, akış tek. Aynı özellik iki parçaya ayrılınca iki ayrı sınıf çıktı. Kural özelliği bütün olarak sınıflamaz; hangi parçanın ölçüldüğüne bakar.

#### Sonradan İndirilen Kod

Bir yayın kısıtı daha vardır, ve bu ders onu ölçmez. Bazı çerçeveler uygulama mağazasından geçmiş paketin dışında, sonradan indirilen kodla davranışı değiştirebilir. Bu, mağaza incelemesini beklemeden düzeltme çıkarmayı vaat eder.

**PG3.** Sonradan indirilen kodun mağaza kuralları içinde nereye kadar izinli olduğu bu derste bir varsayımdır ve ölçülmez. Sınır mağazanın kurallarına bağlıdır, kurallar değişebilir ve platformdan platforma ayrılabilir. Ekip bu yolu kullanmadan önce kendi yayınının geçerli kurallarına bakmalıdır; ders bir sınır yazmaz.

Paylaşım sorusu açısından bu yol bir şey değiştirmez: sonradan indirilen kod da bir kod tabanında durur ve kural onu aynı üç soruyla sınıflar.

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

Son sınırı bir bayrak çizer. Güncelleme kodunu iki pakete de koyalım, ama iOS paketinde sabit bir bayrakla kapatalım. Kod aynı işlevdir; bayrak platforma göre açık ya da kapalıdır.

```javascript
// main.mjs — ayni guncelleme kodu iki pakete girer, bir platformda bayrakla kapali
import { Sharing, countSharing } from "./sharing.mjs";

const states = ["surum-eski", "surum-guncel", "surum-zorunlu"];
const updateEnabled = { ios: false, android: true };

function androidUpdate(state) {
  if (state === "surum-guncel") return "islem-yok";
  return state === "surum-zorunlu" ? "kurulum-baslatildi" : "guncelleme-teklif-edildi";
}

function guardedUpdate(state, platform) {
  if (!updateEnabled[platform]) return "kapali";
  return androidUpdate(state);
}

const single = new Sharing("uygulama-ici-guncelleme", { android: androidUpdate }, states);
const flagged = new Sharing(
  "bayrakli-guncelleme",
  { ios: guardedUpdate, android: guardedUpdate },
  states,
);

single.line();
flagged.line();
countSharing([single, flagged]);
```

```text
uygulama-ici-guncelleme: kod=1 | eksik=ios -> tek
bayrakli-guncelleme: kod=1 | fark=3/3 -> dalli
ortak: 0
dalli: 1
ikiz: 0
tek: 1
```

Aynı özellik iki ayrı sınıf aldı. Bir platformda yazılmışken tek çıktı; iki pakete girip bir platformda bayrakla kapatılınca dallı. Kod bir, çünkü iki platform da aynı `guardedUpdate` işlevini taşıyor. Fark üçte üç, çünkü kapalı platformda her örnek `kapali` döner, açık platformda ise örneğe göre değişen bir sonuç döner.

Bu, ayrımı somutlaştırır. Bayrakla kapalı kod pakete girer ama davranışı yalnız bir platformda çalışır. Kural onu tek saymaz, çünkü işlev iki platformda da vardır; dallı sayar, çünkü sonuç platforma göre ayrışır.

Kural yapıyı görür, bedeli görmez. İki paketin ikisinde de taşınan ve bir tanesinde hiç çalışmayan kod, pakete yük bindirir, sınanması gerekir ve bayrağın unutulduğu gün kullanıcıya ulaşır. Bunların hiçbiri sınıfa yansımaz. Bakım bedelini düzyazı söyler.

**PG4.** Bayrak modelde sabit bir tablodur. Gerçek bir uygulamada bayrak sunucudan gelebilir, sürüme göre değişebilir ya da derleme anında kodu paketten tamamen çıkarabilir; son durumda kod pakete girmez ve ölçülen model değişir. Bu ders yalnız paketin içinde kalan, çalışma anında kapatılan kodu ölçer.

#### Kapanış Tablosu

Kurs on ders boyunca aynı soruyu sordu. Tablo her dersin ölçtüğü özelliği, aldığı paylaşımı ve dersi sınırlayan ölçümü yan yana koyar.

| Ders | Ölçülen özellik | Paylaşım | Sınırlayıcı ölçüm |
|---|---|---|---|
| Çapraz Platform Gerekçesi | Sepet toplamı, iki platformda aynı işlev | ortak | Rozet işlevi üç örnekle ortak; dördüncü örnek eklenince dallı |
| Web Görünümü Tabanlı Yaklaşım | Liste satırını çizen işlev | ortak | Güvenli alan payı kabuktan okununca dallı |
| Köprü Tabanlı Yaklaşım | Düğümü platform bileşenine eşleyen tek işlev | dallı | Eşleme iki ayrı işleve bölününce ikiz |
| Kendi Çizim Motoruyla Yaklaşım | Kutu konumlarını hesaplayan düzen işlevi | ortak | Metin genişliği platform yazı tipinden gelince dallı |
| Paylaşılan Mantık, Yerel Arayüz | Arayüz işlevleri; doğrulama ortak | ikiz | Bir arayüz alanı unutulunca fark sıfırdan büyük, yine ikiz |
| Yaklaşım Seçim Ölçütleri | Yalnız bir platformda yazılan ana ekran parçası | tek | Aynı altı özellik iki düzenekte sayılınca dağılım değişir, tek kalır |
| Platform Tasarım Dilleri Farkı | Ekran türünden gezinme kalıbını seçen tek işlev | dallı | Uyarlanmış tasarımda ortak |
| Yerel Modül İhtiyacı | Biyometrik doğrulama modülü | ikiz | Yalnız bir platformda bulunan yetenek tek |
| Başarım Beklentileri | Küçük resim çözme | ikiz | Aynı hesap ortak kodda tek işlevle ortak; köprü geçişi 1'den 6'ya |
| Mağaza ve Yayın Kısıtları | Uygulama içi güncelleme akışı | tek | Bayrakla kapalı kod dallı |

On dersin dağılımı: ortak 3, dallı 2, ikiz 3, tek 2. Bu, açılış dersindeki ortak 1, dallı 1, ikiz 1, tek 1 dağılımıyla aynı şey değildir. Çekirdek dört özelliği sınadı ve her sınıftan bir örnek verdi; tablo on dersi sayar ve her dersin ana özelliği tek bir satırdır. İki sayı ayrı ölçümlerin sonucudur ve birbirinden türetilmez.

Tabloda dikkat çeken şey, hiçbir sınıfın boş kalmaması ve hiçbirinin baskın çıkmamasıdır. Ama bu bir dağılımdır, bir öneri değildir. Dersler özellik seçimini kendisi yapmıştır; başka özellikler seçilseydi dağılım başka olurdu. Tablo bir yaklaşımın iyi ya da kötü olduğunu söylemez; yalnız her dersin ölçtüğü özelliğin kodunun nasıl paylaşıldığını gösterir.

#### Özet

- Bu dersin paylaşımı: tek. Uygulama içi güncelleme akışı yalnız bir platformda yazıldığı için kural `eksik=ios` bastı ve özelliği tek saydı.
- Güncelleme kararını veren işlev iki platformda aynıdır ve ortaktır; aynı özelliğin iki parçası iki ayrı sınıf alabilir.
- Tek, kodun pakete girmemesi değildir; bir platformda hiç yazılmamış olmasıdır. Pakete girmek ile yazılmış olmak ayrı sorulardır.
- Aynı kod iki pakete girip bir platformda bayrakla kapatılınca özellik dallı oldu; kural yapıyı görür, kapalı kodun bakım bedelini görmez.
- Sonradan indirilen kodun mağaza kuralları içindeki sınırı bu derste bir varsayımdır ve ölçülmez.

#### Kurs Kapanışı

Kurs tek bir soruyu sordu: bu özelliğin kodu platformlar arasında nasıl paylaşılıyor? Kapanış tablosu, dört cevabın dördünün de verildiğini gösterir: ortak, dallı, ikiz ve tek. Her cevap en az iki derste gerçek bir ölçümle karşılandı. Kursun kazanımı bu ayrımı bir özelliğin kodu üzerinde yapabilmek, ve ayrımın kuralın yapıyı gördüğünü, bedeli görmediğini bilmektir. Sıradaki kurs kendi sorusunu kendi açılış dersinde koyar.

