Robert C. Martin’in Clean Code Kitabından Notlar — Bölüm 2: Anlamlı İsimler
Önceki Bölüm : Robert C. Martin’in Clean Code Kitabından Notlar — Bölüm 1 : Temiz Kod
İsimlendirmeler yazılımda çok önemli bir yer tutmaktadır. Değişkenleri, metotları, sınıfları aklınıza gelebilecek daha bir çok şeyi isimlendiriyoruz. Aslında iyi isimlere sahip olabilmek için bazı basit kurallara sahibiz.
Bunlardan ilki niyetimizi açıklayan isimler kulanmak. Bazen iyi isimler seçmek zaman alsa da ilerleyen süreçteki faydaları göz ardı edilemeyecek kadar fazladır. Zaman içerisinde kodunuzu okuyan herkes -siz dahil- daha mutlu olacaksınız. :)
Herhangi bir nesne için yaptığımız isimlendirme aklımıza gelebilecek büyük soruları yanıtlayacak nitelikte olmalıdır. Okuyan kişiye neden var olduğunu, ne yaptığını ve nasıl kullanıldığını anlatmalıdır. Verilen bir isim eğer yorum gerektiriyorsa buradaki amacımızı biraz kaçırmış olduğumuzu söyleyebiliriz.
int d; //gün olarak geçen süre.
Buradaki örnekte gördüğümüz gibi d değişkeni bize hiçbir şey ifade etmiyor. Ne zamandan bahsediyor, ne de geçen süreden.
int elapsedTimeInDay;
int daysSinceCreation;
int daysSinceModification;
Niyetimizi ortaya koyan isimler vermek kodun anlaşılması ve ihtiyaç halinde müdahalesini daha kolay hale getirir.
public List<int[]> getThem() {
List<int[]> list1 = new ArrayList<int[]>();
for (int[] x : theList)
if (x[0] == 4)
list1.add(x);
return list1;
}
Buradaki kod bloğunu incelediğimiz zaman başlarda kurduğumuz cümlelere atıfla Neden var? Ne iş yapıyor? ve Nasıl kullanılıyor? sorularından hiç birine cevap bulamıyoruz.
Buradaki kod bloğunun ne iş yaptığını veya neden var olduğunu söylemek neden zor? İncelediğimiz zaman karmaşık ifadeler yok ve girintiler vs. kabul edilebilir durumda. Peki problem nedir?
Buradaki problemimiz kodun basitliği değil dolaylılığıdır.
1- Liste ne içerisiyor?
2- Liste içerisinde bulunan ilk eleman neden önemli?
3- Koşuldaki 4 değeri ne anlam ifade ediyor?
4- Döndürülen listeyi nasıl kullanırım?
Bulunduğumuz noktada kodu nasıl daha iyi bir noktaya getirebiliriz buna bakalım.
public List<int[]> getFlaggedCells() {
List<int[]> flaggedCells = new ArrayList<int[]>();
for (int[] cell : gameBoard)
if (cell[STATUS_VALUE] == FLAGGED)
flaggedCells.add(cell);
return flaggedCells;
}
Kodumuzun aynı basitlikte olduğunu görüyoruz ve Yukarıda sıraladığımız sorularımızı gözden geçirdiğimizde cevapların da kendiliğinden ortaya çıktığını görüyoruz.
Yine kodumuzu bir adım ileriye taşıyabilir ve int dizini, isFlagged gibi ifadelerini için basit sınıf oluşturabilir ve oradan çağırabiliriz.
Her zaman daha iyisi vardır.
Basit ama kötü isimlendirmelere verebileceğimiz başka bir örnek ise değişkenlerimize vereceğimiz küçük L ve büyük O harfleridir. Buradaki sorunumuz tamamen “l” harfinin bir ve “O” harfinin de sıfırı andırmasıdır.
int a = l;if ( O == l )
a = O1;
else
l = 01;
Burayı okuduğumuz zaman bunun bir icat çıkarma olduğunu düşünebiliriz fakat bu tür şeylerin çok fazla olduğu bir kod bloğunu okuduğunuzu düşünün.
Buradaki basit düzeltmenin bizden bir şey götürmeyeceğini düşünün ve iyi isimler verin.
Gürültülü Kelimeler (Noise Words)
Bazen, yalnızda derleyiciyi tatmin etmek için kod yazarken kendimiz için daha büyük problemler yaratıyoruz.
Örneğin aynı scope içerisinde iki farklı şeye atıfta bulunmak istediğimiz zaman aynı isimleri kullanamayacağımız için bilinçli olarak bir yazım yanlışı yaparız ve derleyiciyi memnun etmeyi hedefleriz. city ve ciyt gibi veya city ve theCity gibi. Derleyici memnun olsa da daha sonra kodu okuyacak kişi için bir zorluk yaratmış oluruz.
City isimli bir sınıf üzerinden konuyu ele alalım. cityInfo veya cityData isimlendirmeleri için isimler farklı olsa da benzer şeyler çağrıştırdıkları için bir karmaşıklık ortaya çıkarırlar. Bu isimler yerine departure (kalkış) ve destination (varış) isimlendirmelerini kullanmak bizi daha iyi bir noktaya getirir.
Bu defa da metotlar üzerinden konuşacak olursak;
GetActiveAccount();
GetActiveAccounts();
GetActiveAccountInfo();
Bir geliştirici bu metotlardan hangisini çağıracağını kodu incelemeden veya çok karmaşık bir logic içeriyorsa yazan kişiyi bulmadan nasıl bilebilir ki?
Telaffuz Edilebilir İsimler Kullanın : bkz. getCrrntPrdct() metotunu üzerine konuşmamız gerektiğinde yine kendimize nasıl bir zorluk oluşturduğumuzu görmekteyiz.
class DtaRcrd102 {
private Datetime crtdtm;
private Datetime updtdtm;
private const string rcrdInt= ”102”;
/* … */
};
Hadi kodu elden geçirelim.
class Product{
private DateTime createdTime;
private DateTime updatedTime;
private const string recordId = ”102”;
/* … */
};
Ve sonra da uzun uzun üzerine tartışalım. :)
Aranabilir İsimler Kullanın : Tek harfli isimler ve sayısal sabitler‘in yerlerini bulmak kolay değildir. MAX_CLASSES_PER_STUDENT sabiti kolaylıkla bulunabilirken ifade ettiği 7 sayısını kod içerisinde bulmak daha zahmetli olabilir.
Tek harfli isimlendirmeleri sadece kısa metotlarda yerel değişkenleri isimlendirirken kullanmayı öneriyor Bob Amca.
Bir değişken veya sabit kod bloğu içerisinde birden çok yerde kullanılıyorsa, ona arama dostu bir isim vermek zorunludur.
for (int j=0; j<34; j++) {
s += (t[j]*4)/5;
}
Hadi kodu elden geçirelim.
int realDaysPerIdealDay = 4;
const int WORK_DAYS_PER_WEEK = 5;
int sum = 0;
for (int j=0; j < NUMBER_OF_TASKS; j++) {
int realTaskDays = taskEstimate[j] * realDaysPerIdealDay;
int realTaskWeeks = (realdays / WORK_DAYS_PER_WEEK);
sum += realTaskWeeks;
}
Elden geçirdiğimiz kodları incelediğimiz zaman sum’ın iyi bir isimlendirme olmadığını ancak en azından aranabilir olduğunu unutmayın. Ancak WORK_DAYS_PER_WEEK’in kullanıldığı tüm yerleri bulmanın taşıdığı 5 değerini bulmaya göre ne kadar kolay olduğunu düşünün.
Interface & Implementations : Elimizde bir interface ve bunu implemente edecek somut bir sınıfımız üzerinden konuşalım.
Bu nesnelere ne isimler vermeliyiz? IShapeFactory ve ShapeFactory?
Günümüzde interfacelerin başına “I” eklemek yaygın bir kullanım olsa da ben interfacelerin başına herhangi birşey eklememeyi tercih ediyorum.
Bu durum en iyi ihtimalle dikkat dağıtıcı en kötü ihtimalle çok fazla bilgidir. Kullanıcıların onlara bir interface verdiğimi bilmelerini istemiyorum.
Onların sadece ShapeFactory olduğunu bilmelerini istiyorum.
Eğer interface veya somut sınıflarımdan birini belirtmem gerekiyorsa somut sınıfı belirtmeyi tercih ediyorum. ShapeFactoryImp, hatta kötü bir adlandırmayla CShapeFactory tercih ederim.
Sınıf (Class) İsimleri : Sınıfların isimleri Customer, WikiPage, Account ve AddressParser gibi isim veya isim cümlesi isimlere sahip olmalıdır. Manager, Processor, Data veya Info gibi sözcüklerden sınıfları isimlendirirken kaçının. Sınıfları isimlendirirken fiil kullanmayın.
Metot (Method) İsimleri : Metot isimleri PostPayment(), DeleteProduct(), Save() gibi fiil veya fiil içeren isimlere sahip olmalıdır. Accessors, mutators ve predicates değerlerine göre adlandırılmalı ve java standartlarına göre başlarına get, set ve is eklenmelidir.
string name = employee.getName();
customer.setName(”mike”);
if (paycheck.isPosted())…
Kurucu metotlarda (constructure) aşırı yüklenildiği (overload) durumlarda, bağımsız değişkenleri tanımlayan adlar ile static factory metotlarını kullanın.
Complex fulcrumPoint = Complex.FromRealNumber(23.0);
Genellikle yukarıdaki yaklaşım aşağıdakinden daha iyidir.
Complex fulcrumPoint = new Complex(23.0);
Don’t Be Cute (Sevimli Olma!)
Sadece sizin ve sizin espri anlayışınızı paylaşan kişiler tarafından anlaşılacak durumlardan kaçının. Örneğin HollyHandGrenade (Kutsal El Bombası) her ne kadar şirin görünse de tam olarak ne iş yaptığını açıkladığını söyleyemeyiz. Bu yüzden DeleteItems daha iyi bir isimlendirmedir.
Eğlence yerine netliği tercih edin!
Konsept Başına Bir Kelime Seçin : Soyut bir kavram için bir kelime seçin ve ona bağlı kalın.
Örneğin sahip olmak için Get(), Fetch(), Retrieve() farklı sınıfların eşdeğer metotlar olarak kafa karıştırıcıdır. Hangi metotun hangi sınıfa ait olduğunu hatırlamakla zaman kaybetmeyin.
Aynı şekilde sınıflar içinde geçerlidir. Sınıflar için Controller, Manager veya Driver isimlendirmelerinin aynı kod base içerisinde olması kafa karıştırıcıdır. Birini seçin ve ona sadık kalın.
Buradaki tutarlılık kodumuzu kullanacak olan geliştiriciler için büyük bir nimettir.
Kelime Oyunu Yapma : Aynı kelimeyi iki farklı amaç için kullanmaktan kaçının.
Örneğin elimizde iki sayıyı birbirine ekleyerek sonucu dönen Add() isminde bir metotumuz var. Şimdi ise elimizdeki değeri koleksiyona ekleyen bir metota ihtiyacımız var. Bu durumda yeni metotumuza Add() ismini vermelimiyiz? Add ismini kullanmak tutarlı görünebilir fakat anlam karmaşası oluşturmamak için Insert veya Append isimlerini kullanmalıyız.
Geliştiriciler olarak amacımız kodumuzu mümkün olduğunca kolay anlaşılır hale getirmektir.
Anlamlı Bağlamlar (Context) Oluşturun : firstName, lastName, houseNumber, city, state ve zipCode gibi değişkenlere sahip olduğumuzu düşünelim.
Bir metot içerisinde zipCode’u ele aldığımızda ne anlam ifade ettiği oldukça açıktır.
Peki ya bir metot içerisinde state değişkenini tek başına gördüğümüz zaman aynı şeyi söyleyebilir misiniz?
Bu durum için bağlam ekleyebilirsiniz. addrFirstName, addrLastName, addrState gibi. En azından kodu okuyan diğer insanlar bunun daha büyük bir yapının parçası olduğunu anlayacaklardır.
Tabi bu durumda gereksiz bağlamlar eklemek te kodumuzdaki karmaşayı artıracağını unutmayın.
Elbette bundan daha iyi çözümümüz ise Address isminde bir sınıf oluşturmak ve ortadan kavram karmaşasını kaldırmaktır.
Buradaki kurallardan bazılarını uygulayın ve kodunuzun okunabilirliğini iyileştirip iyileştirmediğini görün.
Böylelikle ikinci bölümümüzün de sonuna geldik.
Okuduğunuz için teşekkür ederim.
Sonraki Bölüm : Robert C. Martin’in Clean Code Kitabından Notlar — Bölüm 3: Fonksiyonlar