Clean Code Cookbook Kitabı
Yakın zaman önce elime geçen ve inceleme fırsatı bulduğum güzel bir kitaptan birkaç maddeyi, okuduğum kadarıyla, sizlerle paylaşmak istiyorum. Clean Code Cookbook kitabının orjinali şuradan bulabilirsiniz.
Faydası olması dileğiyle…
1. Converting Anemic Objects to Rich Objects(Solgun Nesneleri Zengin Nesnelere Dönüştürme)
Dış müdahalelere karşı nesnelerimizi korumalı ve veri yerine davranışı dışarı açmalıyız. Ayrıca, yapısal değişiklik isteklerini sınırlandırmaya çalışmalıyız.
Önce:
public class Song {
String name;
String authorName;
String albumName;
}
Sonra:
Attribute’lar(alanlar) private olmalı ve nesneleri kendi zengin nesnelerine çevirmeliyiz.
public class Song {
private String name;
private Artist author;
private Album album;
public String albumName() {
return album.name();
}
}
2. Identifying the Essence of Your Objects(Nesnelerinizin Özünü Belirleme)
Nesnelerimizden değişmez objeler tanımlamayı ve onları her zaman valid(geçerli) tutmaya çalışırız.
Önce:
const date = new Date();
date.setMonth(4);
Sonra:
Koddaki davranışların öz niteliklerinin değişimine izin vermemeliyiz. Nesne ilk oluşturulduğu zaman, bunları da tanımlamış olmalıyız.
const date = new ImmutableDate("2025-01-01");
3. Removing Setters from Objects(Nesnelerden Setter’ları Kaldırma)
Nesnelerimizi harici manipülasyonlardan korumak istiyorsak ve kapsüllemeyi(encapsulation) doğru bir şekilde uygulamak istiyorsak, setter metodlarını kaldırmalı ve değişmezliği(immutability) sağlamalıyız.
Önce:
public class Point {
protected int x;
protected int y;
public Point() { }
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
}
Point location = new Point();
location.setX(1);
location.setY(2);
Sonra:
Koddaki setter’lar kaldırmalıyız.
public class Point {
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
Point location = new Point(1, 2);
4. Removing Getters(Getter’leri Kaldırma)
Kazara kendi iç yapınızı dışarı açmamak ve sonrasında yazılımı değiştirme özgürlüğünü kaybetmemek için getter metodlarını kaldırmalıyız. Verinizi doğrudan açmak yerine davranışlara dayalı metodları kullanmalıyız.
Önce:
final class Window {
private int width;
private int height;
private List<Integer> children;
public int getWidth() {
return this.width;
}
public int getArea() {
return this.width * this.height;
}
public List<Integer> getChildren() {
return this.children; // Kontrol dışı ekleme ve çıkarma yapılmasına neden olur
}
}
Sonra:
Özellikle liste gibi değiştirebilir elemanları doğrudan dışarı açmamalıyız. Bunları kullanan iş mantığı metodları ile yönetmeliyiz.
final class Window {
private int width;
private int height;
private List<Integer> children;
public int getArea() {
return this.width * this.height;
}
public void addChildren(Integer child) {
this.children.add(child);
}
// illa children listesini döndürmek istiyorsak, buna benzer şekilde değişmez yapmamız gerekiyor
public List<Integer> getChildren() {
return Collections.unmodifiableList(this.children);
}
}
5. Removing Side Effect(Yan Etkilerin Giderilmesi)
Yan etkiler bağımlılık(coupling) getirir ve beklenmeyen sonuçlar doğurur. Çok-çekirdekli(multi-processing) işlemlerde hatalı sonuçlar üretir.
Önce:
let counter = 0;
function incrementCounter(value: number): void {
counter += value;
console.log(`Counter is now ${counter}`);
}
Sonra:
Test edimesi kolay, herhangi bir harici parametreyi kullanmayan sadece kendi parametrelerini kullanan yan etkisiz yapılar kurmaya özen göstermeliyiz.
function incrementCounter(counter: number, value: number): number {
return counter + value;
}
6. Narrowing Reused Variables(Yeniden Kullanılan Değişkenlerinin Kullanım Alanlarını Daraltma)
Değişkenlerin kullanım alanlarını minimum olacak şekilde tutmalı ve aynı değişkeni kullanmamalıyız. Kodda takip etmek zorlaşır.
Önce:
double total = item.getPrice() * item.getQuantity();
System.out.println("Line total: " + total);
total = order.getTotal() - order.getDiscount();
System.out.println( "Amount due: " + total );
// 'total' değişkeni tekrar kullanılmış
Sonra:
Farklı değişkenler hatta farklı kod blokları kullanmalıyız. Niyetimizi iyi anlatan, spesifik isimler seçmeliyiz.
function printLineTotal() {
double lineTotal = item.getPrice() * item.getQuantity();
System.out.println("Line total: " + lineTotal);
}
function printAmountTotal() {
double amountTotal = order.getTotal() - order.getDiscount();
System.out.println("Amount due: " + amountTotal);
}
7. Removing Versioned Methods/Codes (Versiyonlanmış Metodları/Kodları Kaldırma)
Git gibi kontrol yönetim sistemleri varken, bu tarz eklentiler yapmamalıyız. Git local history’de commitlemediğimiz değişiklikler dahi gözükür.
Önce:
findMatch()
findMatch_new()
findMatch_newer()
findMatch_newest()
findMatch_version2()
Sonra:
Kodun her zaman son hali olmalı, diğer versiyonları değişiklik geçmişinden takip etmeliyiz.
findMatch()
8. Removing Double Negatives(Çift Negatifleri Kaldırma)
Değişkenlere, metodlara ve sınıflara pozitif isimler vermek, kodun anlaşılmasını kolaylaştırır.
Önce:
if (!work.isNotFinished())
Sonra:
Beynimiz, pozitif durumları daha iyi anlar.
if (work.isDone())
9. Replacing Explicit Iterations(Açık İterasyonların Değiştirilmesi)
Index ihtiyacı olmayan yerlerde, daha yüksek seviye iterasyonların kullanılması hata riskini azaltır.
Önce:
for (let i = 0; i < colors.length; i++) {
console.log(colors[i]);
}
Sonra:
Anlaşılması daha kolay.
colors.forEach((color) => {
console.log(color);
});
10. Replacing Magic Numbers with Constants(Sihirli Sayıların Sabitlerle Değiştirilmesi)
Açıklaması olmayan sihirli sayılardan uzak durmalıyız. Kaynağını bilmediğimiz için, kodun sonraki versiyonlarda hata yapmamıza neden olur. Okunması, anlaşılması ve test edilmesi zor olur.
Önce:
function energy($mass) {
return $mass * (299792 ** 2);
}
Sonra:
Anlaşılması daha kolay.
function energy($mass) {
return $mass * (LIGHT_SPEED_KILOMETERS_OVER_SECONDS ** 2);
}
11. Documenting Regular Expressions(Düzenli İfadeleri Belgeleme)
Karmaşık düzenli-ifadeleri daha kısa ve açıklayıcı örneklere ayırmalıyız.
Önce:
val regex = Regex("^\\+(?:[0-9][- -]?){6,14}[0-9a-zA-Z]$")
Sonra:
Anlaması ve debug etmesi daha kolay olur.
val prefix = "\\+"
val digit = "[0-9]"
val space = "[- -]"
val phoneRegex = Regex("^$prefix(?:$digit$space?){6,14}$digit$")
12. Avoiding Callback Codes(Callback Kodlarından Kaçınma)
Callback kodlarının okunması ve anlaşılması zordur. Mümkün olduğunda, sıralı ifadeler kullanmalıyız.
Önce:
asyncFunc1(function (error, result1) {
if (error) {
console.log(error);
} else {
asyncFunc2(function (error, result2) {
if (error) {
console.log(error);
} else {
asyncFunc3(function (error, result3) {
if (error) {
console.log(error);
} else {
// İçiçe ifadeler...
}
});
}
});
}
});
Sonra:
Hemen hemen her dilde olan async/await benzeri yapılarla, içiçe olan kodları tek sıra haline getiririz.
function asyncFunc1() {
return new Promise((resolve, reject) => {
// Async operation
// ...
// If successful
resolve(result1);
// If error
reject(error);
});
}
function asyncFunc2() {
return new Promise((resolve, reject) => {
// Async operation
// ...
// If successful
resolve(result2);
// If error
reject(error);
});
}
async function performAsyncOperations() {
try {
const result1 = await asyncFunc1();
const result2 = await asyncFunc2();
const result3 = await asyncFunc3();
// Continue with further operations
} catch (error) {
console.log(error);
}
}
performAsyncOperations();
Devamını da fırsat buldukça ekleyebilirim. Sağlıcakla kalın.