---
title: 'Yeniden Düzenleme Teknikleri'
source: 'https://academia.sh/tr/kurslar/temiz-kod/yeniden-duzenleme-teknikleri'
course: 'Temiz Kod'
language: tr
updated: '2026-08-17T18:10:37+00:00'
license: 'CC BY-SA 4.0'
---

# Yeniden Düzenleme Teknikleri

Ayıklama, taşıma ve yeniden adlandırma tekniklerinin ücret kitaplığına ara adımlarıyla uygulanması; her adımdan sonra test koşumunun alınması ve adımların kapsam, dosya ve ad geçiş sayısıyla ölçülmesi.

Önceki ders genelleştirmeyi atıp gelen gereksinimi doğrudan karşıladı. Kalan dosya
çalışıyor ama üç kusuru var: bölge katsayısı tablosu tarifeye ait bilgiyi ücret dosyasında
tutuyor, dönem hesabındaki eşik karşılaştırması adsız duruyor ve `g` adı bir gönderiyi
temsil ettiğini söylemiyor. Bu ders üç kusuru üç mekanik teknikle giderir — **ayıklama**
(extract), **taşıma** (move) ve **yeniden adlandırma** (rename) — her tekniği ara
adımlarıyla uygular ve her adımdan sonra koşum çıktısını gösterir.

Test odaklı geliştirme konusunda kırmızı–yeşil–yeniden düzenle döngüsü kurulmuştu: orada
testler tasarımı sürüklüyor, üçüncü aşama düzenlemeye ayrılıyordu. Burada konu döngü değil,
o üçüncü aşamada uygulanan **tekniklerin kendisi**. Bu derste tek bir test yazılmayacak,
tek bir test beklentisi değişmeyecek ve hiçbir koşum kırmızıya dönmeyecek; testler yalnız
davranışın korunduğunu bildiren ölçü aracıdır.

Başlangıç noktası önceki dersin bitiş durumudur.

```js
// tarife.mjs — kademe bilgisinin tek kaynagi
export const KADEME = [
  { ustGram: 1000, kurus: 4990, ad: 'kucuk' },
  { ustGram: 5000, kurus: 7490, ad: 'orta' },
  { ustGram: 10000, kurus: 9900, ad: 'buyuk' },
  { ustGram: 20000, kurus: 12900, ad: 'cok-buyuk' },
  { ustGram: Infinity, kurus: 24900, ad: 'agir' },
];

export function kademe(gram) {
  return KADEME.find((k) => gram <= k.ustGram);
}
```

```js
// ucret.mjs — onceki dersin bitis hali
import { kademe } from './tarife.mjs';

const BOLGE_KATSAYI = { B1: 1.0, B2: 1.25, B3: 1.6 };
const ASGARI_KURUS = 6990;
const INDIRIM_ESIGI = 50;
const INDIRIM_ORANI = 0.88;

export function etkinAgirlik(g) {
  return Math.max(g.gram, Math.ceil((g.en * g.boy * g.yukseklik) / 3));
}

export function gonderiUcreti(g) {
  const bolgeli = Math.round(kademe(etkinAgirlik(g)).kurus * BOLGE_KATSAYI[g.bolge]);
  return Math.max(bolgeli, ASGARI_KURUS);
}

export function donemUcreti(gonderiler) {
  const toplam = gonderiler.reduce((t, g) => t + gonderiUcreti(g), 0);
  if (gonderiler.length <= INDIRIM_ESIGI) return toplam;
  return Math.round(toplam * INDIRIM_ORANI);
}
```

```js
// ucret.test.mjs — onceki dersten devralinan alti test
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { gonderiUcreti, donemUcreti } from './ucret.mjs';

const kutu = { en: 20, boy: 15, yukseklik: 10 };
const donem = (adet) => Array.from({ length: adet }, () => ({ ...kutu, gram: 3000, bolge: 'B1' }));

test('kademe ucreti agirliktan okunur', () => {
  assert.equal(gonderiUcreti({ ...kutu, gram: 3000, bolge: 'B1' }), 7490);
});

test('bolge katsayisi taban ucreti carpar', () => {
  assert.equal(gonderiUcreti({ ...kutu, gram: 3000, bolge: 'B3' }), 11984);
});

test('asgari ucret bolge carpimindan sonra uygulanir', () => {
  assert.equal(gonderiUcreti({ ...kutu, gram: 500, bolge: 'B2' }), 6990);
});

test('hacim agirligi gercek agirliktan buyukse o kullanilir', () => {
  assert.equal(gonderiUcreti({ en: 40, boy: 30, yukseklik: 30, gram: 500, bolge: 'B2' }), 16125);
});

test('esigi asan donemde butun gonderiler indirimli toplanir', () => {
  assert.equal(donemUcreti(donem(60)), Math.round(60 * 7490 * 0.88));
});

test('esigin altindaki donemde indirim uygulanmaz', () => {
  assert.equal(donemUcreti(donem(10)), 10 * 7490);
});
```

```sh
node --test --test-reporter=tap ucret.test.mjs | grep -E '^ *(ok|# (tests|pass|fail))'
```

```
ok 1 - kademe ucreti agirliktan okunur
ok 2 - bolge katsayisi taban ucreti carpar
ok 3 - asgari ucret bolge carpimindan sonra uygulanir
ok 4 - hacim agirligi gercek agirliktan buyukse o kullanilir
ok 5 - esigi asan donemde butun gonderiler indirimli toplanir
ok 6 - esigin altindaki donemde indirim uygulanmaz
# tests 6
# pass 6
# fail 0
```

## Üç Tekniğin Ortak Biçimi

Üç teknik de aynı üç parçadan oluşur. **Önkoşul:** düzenlemenin davranışı değiştirmediğini
gösterecek bir koşum vardır ve yeşildir. **Ara durum:** eski ile yeni bir süre birlikte
durur; bu aralıkta kod çalışır, testler yeşildir, ama yapı iki yerlidir. **Kaldırma:**
eskisi silinir ve ara durum kapanır.

Ara durumun varlığı tekniklerin can alıcı yanıdır. Bir düzenleme tek hamlede yapıldığında,
koşum kırmızıya döndüğünde hatanın yeni yapıda mı yoksa çağrı yerlerinin geçirilmesinde mi
olduğu ayırt edilemez. Adımlara bölündüğünde her koşum tek bir soruyu yanıtlar.

## Ayıklama

Dönem hesabındaki eşik karşılaştırması adsız duruyor: dönem uzunluğunu `INDIRIM_ESIGI`
ile karşılaştıran satır bir kuralı uyguluyor ama kuralın adını söylemiyor. Ayıklama, bir
ifadeyi ya da deyim öbeğini kendi adı olan bir birime çıkarmaktır.

Birinci adım yeni fonksiyonu yazar ve **hiçbir çağrı yerine dokunmaz**.

```js
// ucret.mjs — ayiklama adim 1: yeni fonksiyon yazildi, cagri yeri degismedi
import { kademe } from './tarife.mjs';

const BOLGE_KATSAYI = { B1: 1.0, B2: 1.25, B3: 1.6 };
const ASGARI_KURUS = 6990;
const INDIRIM_ESIGI = 50;
const INDIRIM_ORANI = 0.88;

export function etkinAgirlik(g) {
  return Math.max(g.gram, Math.ceil((g.en * g.boy * g.yukseklik) / 3));
}

export function indirimOrani(adet) {
  return adet > INDIRIM_ESIGI ? INDIRIM_ORANI : 1;
}

export function gonderiUcreti(g) {
  const bolgeli = Math.round(kademe(etkinAgirlik(g)).kurus * BOLGE_KATSAYI[g.bolge]);
  return Math.max(bolgeli, ASGARI_KURUS);
}

export function donemUcreti(gonderiler) {
  const toplam = gonderiler.reduce((t, g) => t + gonderiUcreti(g), 0);
  if (gonderiler.length <= INDIRIM_ESIGI) return toplam;
  return Math.round(toplam * INDIRIM_ORANI);
}
```

```sh
node --test --experimental-test-coverage --test-reporter=tap ucret.test.mjs 2>&1 \
  | grep -E '^# ((tests|pass|fail) |file |ucret\.mjs)'
```

```
# tests 6
# pass 6
# fail 0
# file       | line % | branch % | funcs % | uncovered lines
# ucret.mjs  |  92.31 |   100.00 |   80.00 | 14-15
```

Koşum yeşil, ama ara durumun bedeli kapsamda görünüyor: satır kapsamı yüzde 100'den 92,31'e,
işlev kapsamı yüzde 80'e düştü ve 14–15. satırlar kapsanmayan olarak listelendi. Yeni
fonksiyon henüz çağrılmıyor. Bu, ayıklamanın yarıda bırakıldığında bıraktığı izdir: kimsenin
kullanmadığı bir isim.

İkinci adım çağrı yerini geçirir ve eski ifadeyi siler.

```js
// ucret.mjs — ayiklama adim 2: cagri yeri yeni fonksiyona gecirildi
import { kademe } from './tarife.mjs';

const BOLGE_KATSAYI = { B1: 1.0, B2: 1.25, B3: 1.6 };
const ASGARI_KURUS = 6990;
const INDIRIM_ESIGI = 50;
const INDIRIM_ORANI = 0.88;

export function etkinAgirlik(g) {
  return Math.max(g.gram, Math.ceil((g.en * g.boy * g.yukseklik) / 3));
}

export function indirimOrani(adet) {
  return adet > INDIRIM_ESIGI ? INDIRIM_ORANI : 1;
}

export function gonderiUcreti(g) {
  const bolgeli = Math.round(kademe(etkinAgirlik(g)).kurus * BOLGE_KATSAYI[g.bolge]);
  return Math.max(bolgeli, ASGARI_KURUS);
}

export function donemUcreti(gonderiler) {
  const toplam = gonderiler.reduce((t, g) => t + gonderiUcreti(g), 0);
  return Math.round(toplam * indirimOrani(gonderiler.length));
}
```

```sh
node --test --experimental-test-coverage --test-reporter=tap ucret.test.mjs 2>&1 \
  | grep -E '^# ((tests|pass|fail) |file |ucret\.mjs)'
```

```
# tests 6
# pass 6
# fail 0
# file       | line % | branch % | funcs % | uncovered lines
# ucret.mjs  | 100.00 |   100.00 |  100.00 | 
```

Kapsam geri geldi. Dönem fonksiyonunun gövdesi dört satırdan üç satıra indi ve indirim
kuralı artık kendi adıyla ayrıca sınanabilir durumda. Ayıklamanın ölçülebilir kazancı budur:
sınanabilir birim sayısı arttı, davranış aynı kaldı.

## Taşıma

Bölge katsayısı tablosu ücret dosyasında duruyor ama tarifeye ait bir bilgi: ağırlık
kademeleri gibi ticari bir karar ve aynı merci tarafından değiştiriliyor. Taşıma üç adımda
yapılır, çünkü ithal ilişkisi iki dosyayı birden ilgilendirir.

Birinci adım hedef modülde yeni işlevi oluşturur; kaynak dosyaya dokunulmaz.

```js
// tarife.mjs — tasima adim 1: islev hedef modulde olusturuldu
export const KADEME = [
  { ustGram: 1000, kurus: 4990, ad: 'kucuk' },
  { ustGram: 5000, kurus: 7490, ad: 'orta' },
  { ustGram: 10000, kurus: 9900, ad: 'buyuk' },
  { ustGram: 20000, kurus: 12900, ad: 'cok-buyuk' },
  { ustGram: Infinity, kurus: 24900, ad: 'agir' },
];

const BOLGE_KATSAYI = { B1: 1.0, B2: 1.25, B3: 1.6 };

export function kademe(gram) {
  return KADEME.find((k) => gram <= k.ustGram);
}

export function bolgeKatsayisi(bolge) {
  return BOLGE_KATSAYI[bolge];
}
```

```sh
node --test --test-reporter=tap ucret.test.mjs | grep -E '^# (tests|pass|fail)'
printf 'katsayi tablosu tarife.mjs: %s ucret.mjs: %s\n' \
  "$(grep -c 'B2: 1.25' tarife.mjs)" "$(grep -c 'B2: 1.25' ucret.mjs)"
```

```
# tests 6
# pass 6
# fail 0
katsayi tablosu tarife.mjs: 1 ucret.mjs: 1
```

Tablo şu anda iki dosyada. Bu, birinci dersteki bilgi yinelemesinin ta kendisi ve bilerek
üretilen geçici bir durumdur; taşımanın sonraki iki adımı bu yinelemeyi kapatmak içindir.

İkinci adım çağrı yerini yeni işleve geçirir. Eski sabit dosyada kalır, artık kullanılmaz.

```js
// ucret.mjs — tasima adim 2: cagri yeri tarifedeki isleve gecirildi
import { kademe, bolgeKatsayisi } from './tarife.mjs';

const BOLGE_KATSAYI = { B1: 1.0, B2: 1.25, B3: 1.6 };
const ASGARI_KURUS = 6990;
const INDIRIM_ESIGI = 50;
const INDIRIM_ORANI = 0.88;

export function etkinAgirlik(g) {
  return Math.max(g.gram, Math.ceil((g.en * g.boy * g.yukseklik) / 3));
}

export function indirimOrani(adet) {
  return adet > INDIRIM_ESIGI ? INDIRIM_ORANI : 1;
}

export function gonderiUcreti(g) {
  const bolgeli = Math.round(kademe(etkinAgirlik(g)).kurus * bolgeKatsayisi(g.bolge));
  return Math.max(bolgeli, ASGARI_KURUS);
}

export function donemUcreti(gonderiler) {
  const toplam = gonderiler.reduce((t, g) => t + gonderiUcreti(g), 0);
  return Math.round(toplam * indirimOrani(gonderiler.length));
}
```

```sh
node --test --test-reporter=tap ucret.test.mjs | grep -E '^# (tests|pass|fail)'
printf 'ucret.mjs icinde BOLGE_KATSAYI gecisi: %s\n' "$(grep -c BOLGE_KATSAYI ucret.mjs)"
```

```
# tests 6
# pass 6
# fail 0
ucret.mjs icinde BOLGE_KATSAYI gecisi: 1
```

Geçiş sayısının bire inmesi kaldırma adımının önkoşuludur: ad yalnız kendi tanımında
geçiyorsa hiçbir yerde okunmuyor demektir. Üçüncü adım tanımı siler.

```sh
grep -v 'BOLGE_KATSAYI' ucret.mjs > gecici.mjs && mv gecici.mjs ucret.mjs
node --test --test-reporter=tap ucret.test.mjs | grep -E '^# (tests|pass|fail)'
printf 'katsayi tablosu tarife.mjs: %s ucret.mjs: %s\n' \
  "$(grep -c 'B2: 1.25' tarife.mjs)" "$(grep -c 'B2: 1.25' ucret.mjs)"
```

```
# tests 6
# pass 6
# fail 0
katsayi tablosu tarife.mjs: 1 ucret.mjs: 0
```

Katsayı tablosu artık tek dosyada. Taşımanın ölçüsü satır sayısı değil, bir bilginin
bulunduğu dosya sayısıdır: ikiden bire indi ve tarife kararlarını değiştiren kişi bundan
sonra tek dosyaya bakacak.

## Yeniden Adlandırma

Taşıma yeni bir kusur üretti. Tarife modülünde şimdi iki arama işlevi var: biri ağırlığa
göre, diğeri bölgeye göre. `kademe` adı, modülde tek arama varken yeterliydi; ikincisi
gelince hangi boyutta arama yaptığını söylemez oldu. Yeniden adlandırmanın gerekçesi çoğu
zaman böyle doğar — ad kötüleşmez, adın içinde durduğu bağlam değişir.

Dışa verilen bir ad, onu ithal eden her dosyayı ilgilendirir. Bu yüzden adlandırma da üç
adımdır ve birinci adımda eski ad yaşatılır.

```js
// tarife.mjs — ad adim 1: yeni ad eskisinin yanina eklendi
export const KADEME = [
  { ustGram: 1000, kurus: 4990, ad: 'kucuk' },
  { ustGram: 5000, kurus: 7490, ad: 'orta' },
  { ustGram: 10000, kurus: 9900, ad: 'buyuk' },
  { ustGram: 20000, kurus: 12900, ad: 'cok-buyuk' },
  { ustGram: Infinity, kurus: 24900, ad: 'agir' },
];

const BOLGE_KATSAYI = { B1: 1.0, B2: 1.25, B3: 1.6 };

export function agirlikKademesi(gram) {
  return KADEME.find((k) => gram <= k.ustGram);
}

export const kademe = agirlikKademesi;

export function bolgeKatsayisi(bolge) {
  return BOLGE_KATSAYI[bolge];
}
```

```sh
node --test --test-reporter=tap ucret.test.mjs | grep -E '^# (tests|pass|fail)'
printf 'eski ad gecisi: %s\n' "$(cat tarife.mjs ucret.mjs | grep -o 'kademe' | wc -l | tr -d ' ')"
```

```
# tests 6
# pass 6
# fail 0
eski ad gecisi: 3
```

İkinci adım çağrı yerlerini yeni ada geçirir. Aynı adımda ikinci bir yeniden adlandırma
yapılıyor: `g` parametresi `gonderi` oluyor. İki adlandırmanın aynı adıma sığması bir
gevşeklik değil, kapsam farkının sonucudur; `g` adının kapsamı tek fonksiyondur, dışarıdan
kimse onu görmez, dolayısıyla ara adıma ihtiyaç duymaz.

```js
// ucret.mjs — ad adim 2: cagri yeri yeni ada gecirildi
import { agirlikKademesi, bolgeKatsayisi } from './tarife.mjs';

const ASGARI_KURUS = 6990;
const INDIRIM_ESIGI = 50;
const INDIRIM_ORANI = 0.88;

export function etkinAgirlik(gonderi) {
  const hacim = Math.ceil((gonderi.en * gonderi.boy * gonderi.yukseklik) / 3);
  return Math.max(gonderi.gram, hacim);
}

export function indirimOrani(adet) {
  return adet > INDIRIM_ESIGI ? INDIRIM_ORANI : 1;
}

export function gonderiUcreti(gonderi) {
  const taban = agirlikKademesi(etkinAgirlik(gonderi)).kurus;
  return Math.max(Math.round(taban * bolgeKatsayisi(gonderi.bolge)), ASGARI_KURUS);
}

export function donemUcreti(gonderiler) {
  const toplam = gonderiler.reduce((t, g) => t + gonderiUcreti(g), 0);
  return Math.round(toplam * indirimOrani(gonderiler.length));
}
```

```sh
node --test --test-reporter=tap ucret.test.mjs | grep -E '^# (tests|pass|fail)'
printf 'eski ad gecisi: %s\n' "$(cat tarife.mjs ucret.mjs | grep -o 'kademe' | wc -l | tr -d ' ')"
```

```
# tests 6
# pass 6
# fail 0
eski ad gecisi: 1
```

Üçte birden bire indi; kalan tek geçiş, eski adı yaşatan bağdır. Üçüncü adım onu siler.

```sh
grep -v 'export const kademe = agirlikKademesi;' tarife.mjs > gecici.mjs
mv gecici.mjs tarife.mjs
node --test --test-reporter=tap ucret.test.mjs | grep -E '^ *(ok|# (tests|pass|fail))'
printf 'eski ad gecisi: %s\n' "$(cat tarife.mjs ucret.mjs | grep -o 'kademe' | wc -l | tr -d ' ')"
```

```
ok 1 - kademe ucreti agirliktan okunur
ok 2 - bolge katsayisi taban ucreti carpar
ok 3 - asgari ucret bolge carpimindan sonra uygulanir
ok 4 - hacim agirligi gercek agirliktan buyukse o kullanilir
ok 5 - esigi asan donemde butun gonderiler indirimli toplanir
ok 6 - esigin altindaki donemde indirim uygulanmaz
# tests 6
# pass 6
# fail 0
eski ad gecisi: 0
```

Test adlarında "kademe" sözcüğü duruyor ve durması gerekiyor: orada geçen şey bir kod adı
değil, alanın sözcüğü. Yeniden adlandırma sembolü değiştirir, alanın sözlüğünü değiştirmez.

## Adımların Bilançosu

| Adım | Teknik | Koşum | Ölçülen |
|---|---|---|---|
| 1 | ayıklama | yeşil | işlev kapsamı %100 → %80 |
| 2 | ayıklama | yeşil | işlev kapsamı %80 → %100 |
| 3 | taşıma | yeşil | katsayı tablosu 1 dosya → 2 dosya |
| 4 | taşıma | yeşil | eski sabitin geçiş sayısı 2 → 1 |
| 5 | taşıma | yeşil | katsayı tablosu 2 dosya → 1 dosya |
| 6 | adlandırma | yeşil | eski adın geçişi 3 |
| 7 | adlandırma | yeşil | eski adın geçişi 3 → 1 |
| 8 | adlandırma | yeşil | eski adın geçişi 1 → 0 |

Sekiz adımın sekizinde koşum yeşil bitti ve test dosyası hiç değişmedi: altı test, altı
beklenti, sıfır düzenleme. Ölçülerin ikisi bir adımda geriye gitti — üçüncü adımda tablo
iki dosyaya çıktı, birinci adımda kapsam düştü. İkisi de ara durumun izidir ve bir sonraki
adımda kapandı. Yeniden düzenlemenin adım adım yapılmasının anlamı burada görünür: ara
durumlar kaçınılmazdır, tek çare onları kısa tutmaktır.

## Özet

- Ayıklama, taşıma ve yeniden adlandırma aynı üç parçadan oluşur: yeşil bir önkoşul koşumu,
  eski ile yeninin birlikte durduğu ara durum ve kaldırma adımı.
- Ayıklamanın ara durumu kapsamda görünür; çağrı yeri geçirilmeden önce işlev kapsamı yüzde
  80'e, satır kapsamı yüzde 92,31'e düştü ve iki satır kapsanmayan olarak listelendi.
- Taşımanın ara durumu bilerek üretilmiş bir bilgi yinelemesidir; katsayı tablosu bir
  dosyadan ikiye çıktı, çağrı yeri geçirildikten sonra bire indi.
- Kaldırma adımının önkoşulu ölçülebilir: eski adın geçiş sayısı bire indiyse ad yalnız
  kendi tanımında duruyor demektir.
- Dışa verilen adlar üç adımda, kapsamı tek fonksiyon olan adlar tek adımda değiştirilir;
  fark adın kapsamından gelir.
- Sekiz adımın sekizi yeşil bitti ve altı testin hiçbiri değişmedi; teknikler davranışı
  değil yalnız yapıyı değiştirdi.

## Sonraki Adım

Bu derste her adımın ardından alınan yeşil koşum, davranışın korunduğunun kanıtı sayıldı.
Bu sayışın kendisi sınanmadı: altı test, ücret hesabının bütün davranışını gerçekten
kapsıyor mu? Sonraki ders testsiz yapılan bir ayıklama adımında sessizce değişen bir
davranışı gösterir, aynı adımın mevcut altı testle de yakalanmadığını ölçer, testlerin
yakalama gücünü sayıya döker ve güçlendirilmiş kümenin aynı adımı kırmızıya çevirdiği anı
gösterir.
