İçeriğe geç
academia.sh

Ders 03 / 10

Köprü Tabanlı Yaklaşım

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.

Yayın Güncelleme

Bu derste

  • Köprü tabanlı yaklaşımın ortak kodu, köprüyü ve yerel görünüm ağacını nasıl ilişkilendirdiğini açıklar
  • Düğümü platform bileşenine eşleyen tek işlevin neden dallı çıktığını ölçer
  • Dallı paylaşımın yineleme ya da sıradan değil platformun değişmesinden doğduğunu gösterir
  • Aynı eşlemenin iki ayrı işleve bölünmesinin paylaşımı dallıdan ikize çevirdiğini ölçer
İçindekiler

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

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

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

// 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);
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.

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

// 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);
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.

// 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);
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.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ ↵ Esc gezin · aç · kapat