Refactoring Nedir? - Bölüm 1

Refactoring, kodun işlevselliğini değiştirmeden, kodun kalitesinin artırılması sürecidir. Evet refactoring bir süreçtir. Her ne kadar amaç/niyet her zaman en baştan temiz kod yazmak olsa da, (her ne kadar yazarken en temiz şekilde yazılsa da) değişen ve gelişen kodların zaman içerisinde sürekli optimize edilmesi gerekecektir.

Refactoring nasıl yapılır?

Refactoring yaparken, yapılacak olanların bir sıralamaya göre yapılması önemlidir. Ayrıca diğer bir önemli konu ise bu işi belirli bir ritimle yapmak önemlidir. Yani sırası ile yapılacak olan her küçük değişiklikten sonra test yapmak gerekir. Küçük değişikler, test, küçük değişiklikler, test, … gibi bir ritimle bu işi yapmak en basit ve garantili yoldur. Refactoring’in tanımında da bahsedildiği gibi, amacımız kodun işlevselliğinde hiçbir değişiklik yapmadan, kodların kalitesini en optimum seviyeye getirmek.Kodun işlevselliğinin değişmediğini garanti etmenin en iyi yolu da elbette test yazmaktır.

Refactoring örnek

Basit bir film kiralama senaryosu üzerinden refactoring nedir ve nasıl yapılmalıdır, daha iyi anlamak için bir örnek yapalım. Bu örnekte müşteriler bir film mağazası üzerinden farklı film tiplerine göre kiralama yapacak. Kiralanan film tipine ve kiralama süresine göre bir fiyat hesaplaması ve müşteri için bir açıklama oluşturulması yapılacak. Örnekte kullanılacak olan sınıflar aşağıdaki gibidir.

Movie.cs

Rental.cs

Customer.cs

Örnek kodlar üzerine ilk yorum

Sırası ile yapılacak olan küçük değişikliklerin öneminden bahsetmiştik. Yukarıdaki kodlarda ilk göze çarpan şey Statement() metodunun çok uzun olması. Bu metod bir text açıklama oluşturuyor. Eğer ilerde bir HtmlStatement() adında bir metot ile HTML açıklama oluşturmak istersek bu uzun kodu tekrarlamak gerekecek. Ayrıca yapılacak her değişiklikte(örneğin fiyat hesaplama kuralı değiştiğinde), her iki(veya daha fazla tekrar eden) kodu da değiştirmek gerekecek. Oysaki bir çok ortak kullanılacak kod parçası var. Bundan dolayı, ilk işimiz bu metot üzerinde biraz değişiklik yapmak ve ortak olarak kullanılabilecek kod parçaları oluşturmak olacak.

Refactoring yaparken HtmlStatement() metodunu da göz önünde bulundurarak, ortak kullanılabilecek kodları düşünerek refactoring yaparsak, süreç biraz daha kolaylaşacaktır.

Metot İçine Alma (Extract Method)

Küçük kod parçacıkları her zaman daha yönetilebilirdir. Bu kodlarla çalışmak ve bunları taşımak çok daha kolaydır.

Uzun bir kod bloğunda bulunan, uzun if-else ve switch-case kod blokları, lokal değişkenler ve yorum satırları, büyük ihtimalle refactor gerektirir. Kodlara baktığımızda switch-case içinde bir lokal değişkenin değeri değiştiriliyor. Bu kod blokları Statement() metodunun dışına alınabilir.

Yaptığım her değişikliği karşılaştırmalı bir şekilde resim olarak ekleyeceğim. Böylece silinen ve eklenen kısımlar rahatça görülebilir.

Customer.cs - Extract Method
Customer.cs - Extract Method

Oluşturduğumuz ayrı metot içindeki lokal değişkenin ismini de değiştirmeliyiz. Çünkü artık bu değişken bulunduğu kapsam içinde geçerli, dışarıdan bağımsız bir değişkendir. Dolayısı ile kafa karışıklığını önlemek için yeniden adlandırma gereklidir.

Customer.cs - Renaming

Metodu Taşı (Move Method)

Dışarı aldığımız metot içerisinde hala bir sıkıntı var. Metot Customer.cs sınıfı içerisinde ama Rental.cs içerisindeki veri ile işlem yapıyor. O zaman neden bu metot Rental.cs içerisinde olmasın.

İlk olarak metodu Rental.cs içerisine taşıyorum.

Rental.cs - Move Method

Customer.cs içindeki metodu da siliyorum.

Customer.cs - Move Method

Metodun adını değiştiriyorum.

Rental.cs - Renaming
Customer.cs - Renaming

Bu değişikliklerden sonra rental nesnesini metoda parametre olarak geçmeye gerek kalmadı, çünkü artık metot bu sınıfa ait ve bulunduğu sınıfın verileri ile işlem yapıyor.

Bir sınıfın özellikleri ve alanları ile ilgili bir fonksiyonellik olacaksa, bu fonksiyonellik ilgili sınıf içerisinde tanımlanmalıdır.

Geçici Değişkenlerin Yerine Sorgu Yaz (Replace Temp with Query)

Yapılan son değişikliklerden sonra Statement() metodu içerisindeki thisAmount geçici değişkeni artık gereksiz. Bunun yerine yeni taşıdığımız GetCharge()metodunu kullanabiliriz.

Customer.cs - Replace Temp with Query

Geçici değişkenler her zaman problem olabilir. Özellikle kod akışı içerisinde, bu değişkenlerin başına neler gelmiş bilemeyebiliriz. Takip etmesi de zordur. Yukarıdaki örnekte, geçici değişkenin yerine metot yazdık ama bu da iki kez hesaplama yapmaya sebep oldu. Bu bir performans sorununa yol açabilir ama bunu Rental.cs içerisinde optimize etme şansımız var.

Frequent Renter Points İçin de Aynı İşlemlerin Yapılması

Yukarıdaki işlemlerin tamamını bir de frequentRenterPoints için yapabiliriz. Son değişiklikler aşağıdaki gibidir.

Customer.cs - Extract and Move
Rental.cs - Extract and Move

Geçici Değişkenlerin Temizlenmesi (Removing Temps)

Kodlarda hala geçici lokal değişkenler var. Mümkün olduğu kadar bu değişkenleri de temizlemeliyiz.

Metotları, değerlerini değiştirdikleri sınıfların içine aldık. GetCharge() metodu Rental.cs içindeki tanımlandı, çünkü bu sınıfa ait verileri değiştiriyor. Ama GetTotalCharge() metodunu Customer.cs içinde tanımladık. Toplam fiyatı hesaplamak bu sınıfın işidir. Çünkü kaç tane kiralama olduğunu bu sınıf biliyor, Rental.cs sınıfının bu sayıdan haberi yok.

Rental.cs - Removing Temps
Customer.cs - Removing Temps

Şimdi kodlar biraz daha temiz görünüyor. Ama hala bir problem daha var. Rental.cs içindeki metotlar hem, bulunduğu sınıfa ait veriler hem de Movie.cs tipi ile işlem yapan switch-case ve if-else blokları bulunuyor. Kodda eğer bu tip kod blokları olacaksa, verisini kullandığı sınıf içerisinde olmalıdır. Bu örnekte ise iki farklı sınıf ile alakalı nitelikler ile işlem yaptığından hangi sınıfı seçeceğimiz konusunda bir seçim yapmalıyız.

Burada seçimi Movie.cs sınıfından yana yapmalıyız. Bunun ilk sebebi, tip bilgileri genelde, değişime daha açık durumdadır ve tip bilgileri değişirse, Rental.cs sınıfı etkilenmemeli. Bundan dolayı bu metotları, Movie.cs sınıfına taşıyorum.

Rental.cs - Move Method
Movie.cs - Move Method

Buraya kadar kodlarda birçok değişiklik yaptık ve kodlarımız artık biraz daha yönetilebilir ve okunabilir duruma geldi. Ama hala eksikler var. Movie.cs sınıfı içerisindeki PriceCode sabitleri kötü bir tasarımın ürünü. Buradaki refactoring kısmını sonraki makaleye bırakıyorum.

Bir sonraki makalede Movie.cs sınıfını kalıtım ile nasıl rafactoring yapabileceğimizi anlatmaya çalışacağım.