Okunabilir Kod Sanatı — 3
Çok Büyük İfadeleri Bölmek
Araştırmalar gösteriyor ki, bir zamanda sadece üç veya dört şeyi aklımızda tutabiliyoruz. Yani daha büyük ifadeli kod, daha anlaşılması zor kod demektir.
Çok büyük ifadeleri daha ufak sindirilebilir parçalara bölün.
- Bu yazı Art of Readable Code kitabından alıntıdır. Orjinal halini buradan alabilirsiniz.
- Yazının önceki bölümünü buradan bulabilirsiniz.
Bir ifadeyi parçalarına ayırmanın en basit yolu, alt-ifadeleri tanımlayan ekstra değişkenler tanımlamak.
Örneğe bakarsak, ifadenin sol tarafından bir şeyler çıkarmak çok zor.
if line.split(':')[0].strip() == "root":
...
Bunu yeni bir değişkene atarsak,
username = line.split(':')[0].strip()
if username == "root":
...
Şimdi sol tarafın ne olduğu daha anlaşılır oldu.
Özet Değişkenler
Bazen ifadeleri özet bir değişkene atamak faydalı olabilir.
if (request.user.id == document.owner_id) {
// user can edit this document..
}
request.user.id == document.owner_id ifadesi çok büyük olmamasına rağmen, 5 değişken içerir. Anlamak biraz zaman alır.
boolean user_owns_document = (request.user.id == document.owner_id);
if (user_owns_document) {
// user can edit this document...
}
De Morgan Kanunu’nu Kullanmak
De Morgan kanunu şöyle özetleyebiliriz,
not ifadesini parantez içine dağıt, and ve or’ları ters çevir.
Bunu kullanarak bazen ifadeler daha basit yazılabilir. Örneğin;
if (!(file_exists && !is_protected)) Error("Sorry,not read file.");
Şu şekilde daha anlaşılır yazabiliriz,
if (!file_exists || is_protected) Error("Sorry,not read file.");
Kısa-Devre Mantığını İhlal Etmek
Birçok dilde, boolean operatörler kısa-devre hesaplama yaparlar. if (a || b) ifadesinde a true olursa, b tarafı hesaplanmaz. Bazı durumlarda, bunu karmaşık hale getirebiliriz.
assert((!(bucket = FindBucket(key))) || !bucket->IsOccupied());
Oysa bunu daha sade hale getirebiliriz.
bucket = FindBucket(key);
if (bucket != NULL) assert(!bucket->IsOccupied());
Büyük İfadeleri Bölmek
Örnek JavaScript koduna bakarsak,
var update_highlight = function (message_num) {
if ($("#vote_value" + message_num).html() === "Up") {
$("#thumbs_up" + message_num).addClass("highlighted");
$("#thumbs_down" + message_num).removeClass("highlighted");
} else if ($("#vote_value" + message_num).html() === "Down") {
$("#thumbs_up" + message_num).removeClass("highlighted");
$("#thumbs_down" + message_num).addClass("highlighted");
} else {
$("#thumbs_up" + message_num).removeClass("highighted");
$("#thumbs_down" + message_num).removeClass("highlighted");
}
};
Bu koddaki, tek ifadeler çok büyük değil ama hepsi bir araya gelince büyük bir ifade oluşturmuş. Ayrıca, DRY(Don’t Repeat Yourself, Kendini Tekrarlama) prensibinden bakarsak, üstteki kodda, bazı sabitler(highlighted gibi) birden fazla yerde tekrarlanmış. Şu şekilde daha sade olurdu,
var update_highlight = function (message_num) {
var thumbs_up = $("#thumbs_up" + message_num);
var thumbs_down = $("#thumbs_down" + message_num);
var vote_value = $("#vote_value" + message_num).html();
var hi = "highlighted";
if (vote_value === "Up") {
thumbs_up.addClass(hi);
thumbs_down.removeClass(hi);
} else if (vote_value === "Down") {
thumbs_up.removeClass(hi);
thumbs_down.addClass(hi);
} else {
thumbs_up.removeClass(hi);
thumbs_down.removeClass(hi);
}
};
Değişkenler ve Okunabilirlik
Değişkenler ile ilgili problemleri üçe ayırabiliriz,
- Daha fazla değişken olursa, takip etmesi daha zor olur.
- Daha geniş değişken kapsamı olursa, takip etmesi daha uzun sürer.
- Değişken değeri çokça değişirse, şimdiki değerini takip etmek daha da zorlaşır.
Gereksiz Değişkenleri Elemek
Örnek bir JavaScript fonksiyonu, dizi içinden değeri temizlemeye yarıyor.
var remove_one = function (array, value_to_remove) {
var index_to_remove = null;
for (var i = 0; i < array.length; i += 1) {
if (array[i] === value_to_remove) {
index_to_remove = i;
break;
}
}
if (index_to_remove !== null) {
array.splice(index_to_remove, 1);
}
};
index_to_remove değişkeni sadece, ara bir sonuç için tutulmuş. Bu tür değişkenler bazen elenebilir,
var remove_one = function (array, value_to_remove) {
for (var i = 0; i < array.length; i += 1) {
if (array[i] === value_to_remove) {
array.splice(i, 1);
return;
}
}
};
Genellikle, iyi bir strateji olarak, mümkün olduğunca görevi çabuk bitirmek gerekiyor.
Kodunuzu Tekrar Düzenleme
Kodu yeniden düzenlemeyi 3 başlıkta toplayabiliriz,
- Programın ana hedefiyle ilgisi olmayan “alakasız alt-problemleri” dışarı çıkarma.
- Kodunuzu tek bir anda bir işi yapacak şekilde tekrardan düzenlenme.
- Kodunuzu ilk kelimeler ifade edin, bu size daha temiz bir çözüm sunacaktır.
Alakasız Alt-Problemleri Çıkarma
Mühendislik tümüyle büyük problemi küçük parçalara bölme ve bunları tekrardan bir araya koyma işidir.
Bunun için yapmanız gerekenler,
- Fonksiyona ve kod bloğuna bakıp, “Bu kodun üst-seviye amacı nedir” diye sorun.
- Her bir kod satırı için, “Bu satır hedefe mi hizmet ediyor? Yoksa, alakasız alt-problemi mi çözüyor?” diye sorun.
- Eğer ilgili satırlar, alakasız bir alt-problemi çözüyorsa, bunları ayrı bir fonksiyona taşıyın.
Kodları ayrı bir fonksiyona taşıma işini belki her gün yapıyorsunuz. Burada ki püf nokta, alakasız alt-problemler için etkin bir şekilde aramakta yatıyor.
Tanıtıcı Örnek: findClosestLocation()
Aşağıdaki fonksiyon, verilen noktaya en yakın lokasyonu bulma işini icra ediyor.
Döngü içindeki kod, alakasız alt-problem üzerinde çalışıyor: iki enlem/boylam noktaları arasında küresel mesafeyi hesaplamak. Bu kısmı spherical_distance() adında bir fonksiyona taşıyalım.
Şimdi kod şuna dönüştü.
Kod artık daha okunabilir. Sonradan eklenen spherical_distance() fonksiyonun da test etmesi daha kolay olur. Ayrıca, farklı yerlerde de kullanılabilir.
Bir Zamanda Tek İş
Bir zamanda çok şey yapan kodun anlaşılması zordur.
Kod bir zamanda tek bir iş yapacak şekilde organize edilmelidir.
Aşağıdaki diyagram süreci gösteriyor. Sol taraf, aynı kod içinde birçok iş yapan kısımları gösteriyor. Sağ taraf ise kod tekrar organize edildikten sonraki hali.
“Fonksiyonlar bir şeyi yapmalı” tavsiyesini biliyorsunuzdur. Bizim tavsiyemiz de benzerdir ama sadece fonksiyon ile sınırlı değildir.
“Bir zamanda tek iş yapma” sürecimizi şöyle belirtebiliriz,
- Kodun yaptığı “işleri” listele. Burada ki “iş” kavramını mümkün olduğunca küçük tutmak gerekiyor.
- Bu ayrı “işleri” mümkün olduğunca ayrı fonksiyonlara ya da ayrı kod bloklarına ayırmaya çalışın.
İşler Küçük Olabilir
Oylama eklentisi yaptığınızı, “Yukarı” ve “Aşağı” butonlarıyla oy verdiğinizi düşünün.
Kullanıcı butonlardan birini tıklayınca, şu JavaScript kodu çalışır.
vote_changed(old_vote, new_vote); // each vote is "Up", "Down"
Fonksiyon, toplam skoru günceller,
var vote_changed = function (old_vote, new_vote) {
var score = get_score(); if (new_vote !== old_vote) {
if (new_vote === 'Up') {
score += (old_vote === 'Down' ? 2 : 1);
} else if (new_vote === 'Down') {
score -= (old_vote === 'Up' ? 2 : 1);
} else if (new_vote === '') {
score += (old_vote === 'Up' ? -1 : 1);
}
}
set_score(score);
};
Kod çok kısa olsa da, birçok işi yapıyor. Kod sadece bir işi(skoru güncellemek) yapıyor gözükse de, aslında iki işi yapmaktadır.
- old_vote ve new_vote sayısal alanlara çevriliyor.
- score güncelleniyor
Her bir işi ayırarak kodu daha basitleştirebiliriz. Aşağıdaki kod ile ilk işi çözebiliriz,
var vote_value = function (vote) {
if (vote === 'Up') {
return +1;
}
if (vote === 'Down') {
return -1;
}
return 0;
};
Şimdi geri kalan kod şuna dönüşür.
var vote_changed = function (old_vote, new_vote) {
var score = get_score();
score -= vote_value(old_vote); // remove the old vote
score += vote_value(new_vote); // add the new vote set_score(score);
};
Görüldüğü gibi yeni kodumuz insanın zihninde çok daha az efor gerektirmektedir.
Nesneden Değerleri Çıkarmak
….