erkanerturk / refactoring-guide-in-turkish

Türkçe refactoring kılavuzu

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

TÜRKÇE REFACTORING KILAVUZU

ÖNSÖZ

Bu dokümanın bir makale olarak değil de GitHub'da olmasının sebebi, herkesin katkılarına açık bir şekilde sürekli güncel bir kılavuz hazırlamak.

Bazı kelimeler Türkçeye çevrilmedi. Bunun sebebi, birçok kelime artık o kalıp içinde daha anlamlı oluyor. Örneğin; Refactoring, Extract Method, Primitive Obsession vs.

İÇİNDEKİLER

REFACTORING NEDİR?

Refactoring, kodun işlevselliğini değiştirmeden, kodun kalitesini artırma, temiz bir hale ve kolay bir tasarıma dönüştürme sürecidir. Refactoring kavramını anlamak için öncelikle temiz ve basit kod nedir, temiz kodu engelleyen, kötü kod yazmaya iten sebepler nelerdir bir diğer deyişle teknik borç nedir teknik borca iten sebepler nelerdir, onu anlamaya çalışalım.

Temiz Kod Nedir?

Temiz kod diğer geliştiriciler için gayet açık ve anlaşılabilirdir.

Değişken isimlendirmeleri, sınıfların ve metotların uzunlukları ve karmaşıklıkları vs. gibi kodun okunmasını, anlaşılmasını ve bakımını zorlaştıran şeylerin olmaması.

Temiz kod, kod tekrarları içermez.

Kod tekrarı, her defasında, aynı değişiklikleri farklı yerlerde yapmaya sebep olur. Her defasında, bir değişiklik yapıldığında, başka nerelerin değişeceğini akılda tutmak gerekir. Kodun anlaşılmasındaki yükü artırır ve süreçleri uzatır.

Temiz kodda gereğinden fazla satır/sınıf/metot olmaz.

Az kod, daha az akılda tutulması gereken şey, daha az bakım yapılması, daha az hata demektir. Olabildiğince kısa ve basit kod her zaman temiz koddur.

Temiz kodun bakım maliyeti düşüktür.

Temiz kod bakımı kolaylaştırır, hız kazandırır ve bakım maliyetini düşürür.

Teknik Borç Nedir?

Hiç kimse, projeye zarar vermek için bilerek kötü kod yazmaz. Herkes elinden gelenin en iyisini yapmak ister. Kötü kod yazmaya iten sebepler vardır. Kötü yazılan kod da, ilerde başımıza dert açabilir.

Teknik borcu anlatmak için, bankadan çekilen kredi örnek verilir. Acil ödemeniz gereken bir borç için, günü kurtarmak adına çekilen kredi, daha sonra daha fazla borç olarak tekrar karşımıza çıkar. Çekilen tutar tekrar faiziyle geri ödenir.

Aynı şekilde, daha hızlı geliştirmek adına; mesela test yazmadan, geliştirilen her özellik, gün geçtikçe bakım maliyetini artırarak, geliştirme hızını da düşürür, ta ki teknik borcu ödeyene kadar.

Peki teknik borca, kötü kod yazmaya iten sebepler nelerdir?

Ticari kaygılar

Bazen iş koşulları, tamamlanmadan önce özellikleri kullanıma sunmaya zorlayabilir. Bu durumda, projenin bitmemiş bölümlerini gizlemek için kodda fazladan (aslında kodun parçası olmayan) satırlar olabilir.

Teknik borcun sonuçlarının anlaşılmaması

İşverenler/yöneticiler, geride teknik borç biriktirdikçe, maliyetin katlanarak arttığını anlamayabilirler. Bundan dolayı da, ekibin refactoring için zaman ayırmasını, vakit kaybı olarak görürler ve değer vermezler.

Ekip üyeleri arasındaki iletişim eksikliği

İletişim eksikliğinden dolayı bilgi, ekip üyeleri arasında sağlıklı dağılmaz veya tecrübeli birisi bilgiyi tecrübesiz olana yanlış aktarabilir. Bundan dolayı da ekip elindeki güncel olmayan, eksik veya yanlış anlaşılmış bilgiyle geliştirme yapabilir.

Uzun süre farklı dallarda (branch) çalışılması

Teknik borcun birikmesine ve birleştirme işleminde daha da artmasına sebep olur. Toplam teknik borç, ayrı ayrı biriken teknik borç toplamından daha büyük olur.

Gecikmeli refactoring

Refactoring gerekli durumlarda, refactoring ertelenirse, düzenlenmesi gereken bu parçaya bağımlı yeni yazılan her kod, eski düzene göre yazılacağından, teknik borç her yeni yazılan kod için de artar. Oysa anında müdahale edilse, arkasından gelen kodlar için de aynı düzenleme gerekmeyecek.

Beceriksizlik & tecrübesizlik

Bazen sadece tecrübesizlikten veya beceriksizlikten kötü kod yazarak, teknik borç biriktiririz.

Refactoring Ne Zaman Yapılmalı?

Üç kural

  1. İlk defa bir şey yapıyorsan, sadece yap.
  2. İkinci defa aynı şeyi yapıyorsan, tekrara düşmekten çekin ama yine de bir şekilde yap.
  3. Üçüncü defa aynı şeyi yapıyorsan, refactor et.

Özellik ekleme

Refactoring, başkalarının kodlarını anlamayı kolaylaştırır. Yeni özellik eklemek için kodun iyi anlaşılması gereklidir. Kod ne kadar temiz olursa o kadar anlaşılır olur. Dolayısıyla yeni özellik eklemeden önce kodlar refactor edilebilir.

Hata düzeltme

Yine hata bulmak için öncelikle kodun iyi anlaşılması lazımdır. Daha iyi anlamak için kodu refactor ederiz. Refactor işlemi sırasında, çoğunlukla hata bulunur.

Kod inceleme (code review)

Kod inceleme, hem kodu yazan hem de inceleyen için en faydalı iştir. Kod inceleme yaparken, hata bulmak daha kolay ve hızlı olur. İlerde yapılabilecek daha büyük hatalar için de, önceden bilgi sahibi olmayı sağlar.

Refactoring Nasıl Yapılır?

Refactoring kodun normal çalışmasında hiçbir değişiklik yapmadan, kodun daha iyi bir hale gelmesi için yapılan değişiklikler şeklinde olmalıdır.

Kod daha temiz hale gelmeli

Refactoring işleminden sonra kodlar hala temiz ve anlaşılır değilse, harcadığımız zaman boşa gitmiş demektir. Bu durumda neden böyle olduğunu anlamaya çalışmak lazım. Refactoring yaparken genelde; küçük değişiklikler yaparken, birden tek seferde çok büyük bir değişiklikler silsilesi içine girince ve üstüne bir de zaman kısıtı varsa işlerin karışmasına sebep olabilir. Bundan dolayı da refactoring sonucunda ortaya çıkan kod pek de temiz bir kod olmaz.

Ancak bazı kod altyapısı o kadar kötüdür ki, ne yaparsanız yapın iyileştirilemeyebilir. İşte bu durumda, kodun yeniden yazılması düşünülebilir. Tabi bunu yapacaksak, kesinlikle ilgili yerlerin testlerinin yazılmış olması şart. Ayrıca biraz zaman ayırmak da gerekli.

Refactoring sırasında yeni bir özellik/fonksiyonelite eklenmemeli

Yeni özellik eklemek için yazılan kodlarla refactoring için yazılan kodlar farklı commit'lerde olmalıdır. Refactoring kodun işlevini değiştirmez, sadece daha iyi hale getirir.

Tüm testler refactoring işleminden sonra başarılı olmalı

Testleri olmayan kodları refactor etmek, refactoring sürecindeki en tehlikeli kısımdır. Refactoring yaptıktan sonra 2 durumda testler başarısız olabilir.

  • Refactoring sırasında hata yaptın ve çok da önemli değil: Devam et ve hatayı düzelt.
  • Testler çok alt seviye kodları test ediyordur. Örneğin, bir sınıfın private metodunu test ediyordur: Bu durumda, sıkıntı testlerdedir. Dolayısıyla testleri de refactor edebiliriz veya yüksek seviye kodları test eden yeni testler yazabiliriz. Tabi bunun en ideal çözümü, BDD-style test yazmakdır.

KODDAN KÖTÜ KOKULAR GELİYOR

Refactoring yapılacak kod aslında kendisi alarm verir. Koddaki kötü kokular, refactoring için ipuçları içerir. Bu kötü kokuların neler olduğunu bilirsek, refactoring yapılacak kodları daha iyi ayırt edebiliriz.

Long Method

Problem

Metodun gövdesinin gereksiz, aşırı, okunamayacak ve bakımı yapılamayacak derecede uzun olması.

Sebep

Yazılımcı her zaman kodu kendisi yazmak ister. Kendi yazmadığı kodlara güvenmez/güvenmek istemez. Bundan dolayıda kod yazmak, kod okumaktan kolay gelir yazılımcıya. Her yeni gelen yazılımcı, bir yandan okumadan kod ekleyip, bir yandan da kullanılmayan kodları silinmeyince, metot gittikçe şişer, okunamaz ve bakımı yapılamaz hale gelir. Okunamayan kod, sürekli yeni kodların eklenmesini tetikleyerek kısır döngü oluşturur.

İki satır kod için yeni bir metoda ihtiyaç yok diye düşünüp, tüm kodu var olan metotlara eklemek, her zaman yazılımcıya daha kolay gelir. Bu da kodu işin içinden çıkılmaz bir hale sokar.

Çözüm

Anlaşılması için illa açıklanması gereken her kod bloğu ayrı bir metoda taşınabilir. Kodların anlaşılması için taşınan metodun ismi, metodun ne yaptığına dair kesin bilgi vermelidir. Böylece metodu kullananlar, metodun ne yaptığını, içindeki kodları okumadan anlayabilir.

Kod içerisindeki koşullu ifadeler, döngüler her zaman, kodları farklı metoda taşımak için birer adaydır. Kullanılabilecek refactoring teknikleri: Extract Method, Temp with Query, Introduce Parameter Object, Preserve Whole Object, Replace Method with Method Object ve Decompose Conditional.

Sonuç

Sürekli büyüyen kodlar için, tekrar eden, aynı işi yapan, belki de yanlış çalışan kodlar olabilir. Bu kodlar içinde hata bulmak, tekrar eden kodları temizlemek ve bakım yapmak çok zor iştir. Bu kötü tasarımdan kurtulursak, tüm bu olumsuzluklardan da kurtuluruz.

Performans

Kodun daha anlaşılır, bakım yapılabilir ve daha az hataya açık olması, her zaman maliyeti düşürür ve geliştirme hızını artırır. Ayrıca daha iyi tasarlanmış bir kod, daha hızlı çalışır. Daha iyi tasarlanmış bir kodda gereksiz kodlarda olmaz. Yani aslında performansın dengelenmesinin yanında, fazladan geliştirme hızı ve maliyet düşüşü sağlamış oluruz.

Large Class

Problem

Bir sınıfta gereğinden fazla alan, metot, özellik ve dolayısıyla fazla kod olması.

Sebep

Uzun metotlardaki gibi, uzun sınıfların oluşmasındaki en büyük sebep, kolay olmasından dolayı, kodu okumak yerine, direk koda ekleme yapmasıdır.

Çözüm

En iyi çözüm en başta sınıfı parçalara ayırmak. Mesela, GUI'nin kullandığı kısımlar ayrı bir sınıfa taşınabilir. Sınıf paralel veya hiyerarşik olarak ayrıştırılabilir. Aynı şekilde, sınıf içinde gruplanabilecek davranışlar, interface olarak da ayrıştırılabilir.

Kullanılabilecek refactoring teknikleri: Extract Class, Extract Subclass, Extract Interface, Duplicate Observed Data.

Sonuç

Sınıfın daha düzenli, daha okunabilir ve daha kolay bakım yapılabilir bir hale gelmesiyle, yazılımcı sınıfın ne yaptığını, içinde neler olduğunu hatırlama zorunluluğundan da kurtulur. Kod daha fazla kontrol altında tutulabildiğinden, kod tekrarlarının da önüne geçilmiş olur.

Primitive Obsession

Problem

Kod içerisinde primitif tiplere, işlerinin dışında sorumluluklar vermek. Örneğin; range diye bir nesne yerine, start ve end diye değişken kullanmak, USER_ADMIN_ROLE = 1 şeklinde, kodun içinde sabit veriler tutmak veya dizi["istanbul", "34"] gibi bir dizi içinde, bir nesnenin her bir alanının tutacağı verileri tutmak.

Sebep

Biraz tembellikten, belki biraz da tecrübesizlikten, başta bir tane veri için, bir nesne oluşturmak yerine, kolay olan yolu yani değişken tanımlama yoluna gideriz. Benzer bir alan daha lazım olduğunda, kodu refactor edip nesneye çevirmek yerine, bu yeni alanı da, başka bir değişkene atarız. Her defasında bu hatayı yaptıkça, sınıflar/metotlar şişer.

Bir diğer hata da kullanımı kolay ve anlaşılır olan değişkenlerde veritabanına ait olabilecek veriler tutmak. En kötüsü ise, bir sınıfın her alanının, bir dizide veri olarak tutulması. Neyse oluşturmak o kadar zor gelirki, bir dizide bu nesnenin her alanı için bir veri tutulur.

Çözüm

Çözüm basit: nesne oluşturmak. Gruplanabilecek olanlar bir nesne(sınıf) çatısı altında toplamalıyız. Sınıfın alanları olabilir, metodun parametreleri olabilir, kendi başına nesne olabilecek bir değişken olabilir veya zaten grup olan dizi elemanları olabilir; bunların hepsi ayrı bir nesne olabilir.

Kullanılabilecek refactoring teknikleri: Replace Data Value with Object, Introduce Parameter Object, Preserve Whole Object, Replace Type Code with Class, Replace Type Code with Subclasses, Replace Type Code with State/Strategy, Replace Array with Object.

Sonuç

Kod daha düzenli, esnek, anlaşılır olur ve bu da kod tekrarının önüne geçer, bakım maliyetini düşürür. Çünkü artık dizi içindeki verilerin neyi ifade ettiğini anlamakla uğraşmayız, birbiri ile ilişkili verileri, tek bir yerden kontrol edebiliriz.

Long Parameter List

Problem

Methodun çok fazla parametre alması.

Sebep

Metot değiştikçe yeni parametreler eklemek gerekebilir. Her yeni parametre ekledikten sonra, önlem alınmazsa parametreler gittikçe çoğalır veya metot kendi içinde sınıfın verilerini kullanmak yerine, onları parametre olarak alabilir. Bunun sebebi bağımlılığı azaltmak ama yine metodun parametreleri artmış olur.

Çözüm

Metot aynı sınıf içindeki verileri parametre alıyorsa buna gerek yok. Parametre geçmek yerine, metot içinde bu veriler kullanılabilir. Bir sınıfın alanlarını tek tek parametre geçmek yerine, sınıfın kendisini parametre geçmek daha mantıklıdır. Diğer bir durum ise, ilişkili olabilecek parametreleri, bir nesneye çevirmek. Örneğin, start ve end parametrelerini range nesnesine çevirip, bunu parametre olarak geçebiliriz.

Kullanılabilecek refactoring teknikleri: Replace Parameter with Method Call, Preserve Whole Object, Introduce Parameter Object.

Sonuç

Daha okunabilir, kısa, bakımı kolay kodlar. Daha okunabilir kod içerisinde, gereksiz kodl tekrarları da daha rahat farkedilir, dolayısıyla, kod tekrarlarından da kurtulunabilir.

Ne zaman göz ardı edilebilir?

Sınıflar arasında gereksiz bağımlılıklar oluşturabilecek her durumda göz ardı edilebilir.

Data Clumps

Problem

Ortak bir sınıfta toplanabilecek değişkenlerin, tek tek parametre olarak kullanılması. Örneğin; Müşteri bilgileri, adres bilgileri, veritabanı bağlantı bilgileri gibi sınıflar oluşturabilir ve ortak parametreleri bu sınıfların özelliği haline getirebiliriz.

Sebep

Sebep Long Parameter List başlığındakilerle aynıdır.

Çözüm

Çözüm de yine Long Parameter List başlığındakiler ile benzerdir. Parametreleri gruplayıp, bir sınıfa taşıyıp, sınıfı parametre olarak geçmek.

Kullanılabilecek refactoring teknikler: Extract Class, Introduce Parameter Object, Preserve Whole Object.

Sonuç

Kodun anlaşılabilirliğini ve organizyonunun kalitesini artırır. Parametreler dağınık olarak, etrafta duracağına, bir sınıf içinde toplanmış olur. Kod tekrarı engellenir, dolayısıyla kod kısalır, okunabilirliği artar ve bakımı kolaylaşır.

Ne zaman göz ardı edilebilir?

Eğer sınıflar arasında gereksiz bir bağımlılık oluşturacaksa göz ardı edilebilir.

Switch Statements

Problem

Okunması/anlaşılması ve bakımı zor switch-case veya gereksiz uzunluktaki if-else ifadeleri.

Sebep

switch-case ifadeleri çok fazla koşul gerektiği durumda birer ihtiyaç olarak kullanılırlar. Gereğinden fazla uzun if-else ifalerinin oluşması da yine, her yeni gelen koşul için, kodda hiç bir değişiklik yapmadan, uç uca eklenen koşullardan dolayı oluşur.

Çözüm

switch-case veya uzun if-else olan yerde büyük ihtimalle bir sıkıntı vardır ve refactor edilmesi gerekebilir ve büyük olasılıkla polymorphism çözüm olabilir.

switch-case ifadeleri genellikle sınıfların daha iyi tasarlanması ile çözülebiliyor. Çözülemediği durumlar, koşul ifadelerinde metotların kullanıldığı durumlardır ve bunların çözümü de farklıdır.

Kullanılabilecek refactoring teknikler: Extract Method, Move Method, Replace Type Code with Subclasses, Replace Type Code with State/Strategy, Replace Conditional with Polymorphism, Replace Parameter with Explicit Methods, Introduce Null Object.

Sonuç

Uygulamanın tasarımı daha iyi bir hal alır ve bakımı kolaylaşır.

Ne zaman göz ardı edilebilir?

Aslında switch-case ifadeleri çok kullanışlıdır ve yerinde kullanıldığında, hız kazandırır. Eğer uygulanın içerisinde, yönetilemeyecek kadar çok dağılırsa ve kodun okunabilirliğini ve bakımını zorlaştıracaksa, switch-case ifadelerinden kaçınılmalıdır. Ama basit kullanımlarda hiçbir sakıncası yoktur.

Ayrıca, Factory design pattern içinde de switch-case ifadeleri kullanmakta bir sakınca yoktur.

Temporary Field

Problem

Bir sınıf içinde, belirli bir kapsam dahilinde geçerli ve birbirine bağımlı olarak tanımlanmış, geçici alanların olması. Bu alanların, sınıfı geneli için ifade ettiği bir şey yoktur. Sadece bazı metotlar bunların değerlerini değiştir ve kullanır. Bu alanların ne için kullanıldığını bulmak çok zordur.

Sebep

Karmaşık algoritmalar, her zaman çok fazla değişken kullanırlar. Bazen bu değişkenleri parametre olarak, metoda geçmek gerekir ve yazılımcı bunun kötü bir tasarım olduğunu bildiği için bunu yapmak istemez ama başka bir kötü tasarım yapabilir. Algoritma için gerekli olan değişkenleri, sınıfın alanları olarak tanımlar ve bu alanlar sadece ilgili algoritma tarafından kullanılır Dolayısıyla bu algoritma dışında bu değişkenlerin hiç bir manası yoktur.

Çözüm

Bu kötü tasarımdan kurtulmak için, ilişkili olan işlemleri farklı sınıfa taşıyabilir veya Null object pattern yöntemini kullanabiliriz.

Kullanılabilecek refactoring teknikleri: Extract Class, Replace Method with Method Object, Introduce Null Object.

Sonuç

Daha rahat okunabilir ve sade kodlar.

Refused Bequest

Problem

Bir sınıfın, kalıtım aldığı sınıfın çok az özelliğini veya metodunu kullanması.

Sebep

Kodun yeniden kullanılması, kalıtım ve benzer tasarım desenlerini kullanma isteği bazen bunların gereksiz kullanımına bizi iter. Ama hiyerarşi kurmak istediğimiz sınıflar çok farklı olabilir. Örneğin birisi ördek, diğeri oyuncak ördek olabilir.

"The Liskov Substitution Principle" şöyle der: Ördeğe benziyor, ördek gibi ses çıkarıyor ama bataryaya ihtiyacı varsa, büyük ihtimalle yanlış bir soyutlama peşindesin.

Çözüm

Kalıtımla da çözülebilir, kalıtım olmadan da çözülebilir. Kalıtım mantıklı değilse, sınıflar paralel hiyerarşiye çekilir.

Kalıtım yapmak uygunsa, kalıtım alan sınıf içindeki gereksiz metotlardan, alanlardan ve özelliklerden kurtulup, başka bir üst sınıf oluşturup, uygun şekilde yeniden hiyerarşi sağlanabilir.

Kullanılabilecek refactoring teknikleri: Replace Inheritance with Delegation, Extract Superclass.

Sonuç

Kodun okunabilirliğini ve organizasyonunu artırır. Artık, neden Chair sınıfının Animal sıfından türediğini merak etmeye gerek yok.

Alternative Classes with Different Interfaces

Problem

Farklı interface kullanan veya hiç interface kullanmayan, ama aynı işi yapan kod bloklarının, sınıfların veya metotların olması. Bu sınıfların içindeki aynı işleri yapan metotların isimleri, imzaları falan farklıdır. Hangisinin ne zaman kullanılacağı tam olarak belli değildir. Birbirlerinin alternatifleri bile olurlar.

Sebep

Yazılımcının kod okumaması, kötü tasarlanmış kodların olması, sürekli yeni yazılımcıların katılmasından kaynaklı olarak, yazılımcıların, muhtemelen böyle bir işlevi yapan kod parçalarından habersiz olarak, aynı işi yapan kod blokları eklemesinden kaynaklanır.

Çözüm

Sınıfların ve metotların isimlerinin, imzalarının ve uygulanmalarının birebir aynı olması sağlanmalıdır. Birebir aynı olduğunda sınıflardan birisinin gereksiz olduğu anlaşılırsa, gereksiz olan silinir. Sınıfların ortak kullandıkları bölümler varsa bunlar, ortak başka bir sınıfa alınabilir.

Kullanılabilecek refactoring teknikleri: Rename Method, Move Method, Add Parameter, Parameterize Method, Extract Superclass.

Sonuç

Daha az ve temiz, kolay okunabilir, kolay bakım yapılabilir, geliştirme maliyeti azalmış kod altyapısı sağlar.

Ne zaman göz ardı edilebilir?

Bazı sınıflar farklı kütüphanelerde olabilir ve bunlar kendi içinde geliştirilir ve versiyonlanırlar. Bu gibi durumlarda, birleştirmek, silmek ve taşımak mantıksızdır.

Divergent Change

Problem

Bir kod bloğunu değiştirirken, kendinizi silsile halinde başka kodları da değiştirirken bulabilirsiniz. Örneğin; bir nesne değiştiği zaman, onun listelendiği, sıralandığı, eklenip, düzenlendiği gibi ilişkili yerlerinde değişmesi gerekebilir.

Sebep

Kötü tasarım ve kötü kod altyapısından veya birbirine aşırı bağımlı kod parçalarının olmasından kaynaklanan bir durum olabilir.

Çözüm

Sınıfların davranışlarını bölmek veya duruma göre eğer aynı işi yapıyorlarsa birleştirmek.

Kullanılabilecek refactoring teknikleri: Extract Class, Extract Superclass, Extract Subclass.

Sonuç

Daha iyi kod organizasyonu, kod tekrarının azaltılması, bakımın kolaylaşması.

Shotgun Surgery

Problem

Kodun çok daha derinlerindeki, büyük bir sıkıntının, bize görünen küçük kısmı. Buzdağının görünen yüzü de diyebiliriz. Küçük gibi görünen bu kötü tasarımı düzeltmeye kalktığında çok farklı yerlerin etkilendiğini görürüz.

Sebep

Genelde sorumlulukların iyi ayrılamamasından (Seperation of Concerns ve Single Responsibility), tasarım desenlerinin acemice ve kötü uygulanmasından kaynaklanabilir.

Çözüm

Çözüm basit, nesneleri ve davranışları kuralına uygun olarak ayırmak, gereksiz nesneleri ve davranışları silmek.

Uygulanabilecek refactoring teknikleri: Move Method, Move Field, Inline Class.

Sonuç

Daha okunabilir, daha kolay bakım yapılabilir, kod tekrarlarının az olduğu, daha iyi bir kod organizasyonu sağlar.

Parallel Inheritance Hierarchies

Problem

Her ne zaman bir sınıf için alt sınıf oluştursan, kendini başka bir sınıf için alt sınıf oluşturma ihtiyacı duyarken bulabilirsin. İlişkili iki sınıf farklı dallardan hiyerarşi oluşturur.

Sebep

Hiyerarşi küçük olduğu sürece çok problem değil ama yeni sınıflar eklenmeye başlandıkça, değişiklik yapmak sürekli zorlaşmaya başlar.

Çözüm

  • Hiyerarşilerin en tepesindeki sınıfların ikisini de kalıtım alan yeni bir hiyerarşi veya tepedeki paralel sınıfların birleşiminden yeni bir hiyerarşi: Move Method ve Move Field.

Sonuç

  • Kod organizasyonu iyileşmesi.
  • Kod tekrarını engellemesi.

Ne zaman göz ardı edilebilir?

Bazen paralel sınıf hiyerarşilerine sahip olmak, yazılımın mimarisiyle daha büyük karışıklıktan kaçınmanın bir yoludur. Hiyerarşileri çoğaltma girişimlerinizin daha çirkin kodlar ürettiğini tespit ederseniz, tüm değişikliklerinizi geri alın ve bu koda alışın.

Comments

Problem

Metodun açıklayıcı yorumlarla dolu olması.

Sebep

Yorumlar genellikle yazılımcının kendi kodunun sezgisel olarak anlaşılmadığını veya açık olmadığını fark etmesiyle, iyi niyetle yazılır. Bu gibi durumlarda, yorumlar, kötü kokan kodun kokusunu gizleyen deodorant gibidir.

En iyi yorum bir metot veya sınıf için iyi bir addır.

Bir kod parçasının yorum yapılmadan anlaşılmayacağını düşünüyorsanız, kod yapısını yorumları gereksiz kılacak şekilde değiştirmeyi deneyin.

Çözüm

  • Bir yorumun karmaşık bir ifadeyi açıklaması amaçlanıyorsa, ifade, anlaşılabilir alt ifadelere bölünmelidir: Extract Variable.
  • Bir yorum kodun bir bölümünü açıklıyorsa, bu bölüm ayrı bir metot olarak yazılabilir. Yeni yöntemin adı, büyük olasılıkla, yorum metninin kendisinden alınabilir: Extract Method.
  • Bir metot zaten oluşturulmuşsa, ancak metodun ne yaptığını açıklamak için yorumlar hala gerekliyse, metoda açıklayıcı bir isim verin: Rename Method.
  • Sistemin çalışması için gerekli olan bir durum hakkında kurallar koymak için yorum yazmak gerekirse: Introduce Assertion.

Sonuç

Kod daha sezgisel ve açık hale gelir.

Ne zaman göz ardı edilebilir?

  • Bir şeyin neden belirli bir şekilde uygulandığını açıklarken.
  • Karmaşık algoritmaları açıklarken (algoritmayı basitleştirmek için tüm diğer yöntemler denendikten sonra).

Duplicate Code

Problem

İki kod parçasının neredeyse aynı olması.

Sebep

Kod tekrarı, birden çok yazılımcının aynı yazılımın farklı bölümlerinde aynı anda çalıştığı durumlarda olur. Farklı işler üzerinde çalıştıklarından, diğer yazılımcının kendi ihtiyaçları için benzer bir kod yazmış olabileceğinin farkında olmayabilir.

Ayrıca, kodun belirli kısımları farklı göründüğü halde aslında aynı işi yaptığı durumlar da vardır. Bu tür bir kod tekrarını bulmak ve düzeltmek zor olabilir.

Bazen kod tekrarı bilerek yapılır. İşin yetişmesi gerek zamanın sonuna geliniyorsa ve mevcut kod gereken iş için "neredeyse doğru" ise acemi yazılımcı, "kopyala-yapıştır" yapmaktan kendini alamayabilir. Bazen de yazılımcı tembellik ederek kod tekrarına göz yumabilir.

Çözüm

Sonuç

  • Tekrar eden kodun birleştirilmesi, kodunuzun yapısını basitleştirir ve daha kısa hale getirir.
  • Sadeleştirme + kısayol = basitleştirmesi kolay ve bakımı daha ucuz kod.

Ne zaman göz ardı edilebilir?

Çok nadir durumlarda, iki kod parçasının birleştirilmesi, kodu daha az sezgisel ve haha az açık hale getirebilir.

Lazy Class

Problem

Bir sınıfın anlaşılması ve bakımı, zaman ve maliyet gerektirir. Dolayısıyla bir sınıf anlaşılmıyorsa ve istekleri yeterince karşılamıyorsa, o sınıf silinmelidir.

Sebep

Belki bir sınıf tamamen işlevsel olacak şekilde tasarlanmıştır, ancak refactoring yaptıktan sonra saçma derecede küçük hale gelmiştir. Veya gelecekte yapılacak ama daha yapılmamış bir özellik için tasarlanmış olabilir.

Çözüm

Sonuç

  • Kod uzunluğunu azaltır.
  • Bakımı kolaylaştırır.

Ne zaman göz ardı edilebilir?

Koddaki basitlik ve açıklık arasınki dengeyi korumak şartıyla, gelecekteki gelişmelere yönelik niyetleri betimlemek için bir Lazy Class oluşturulabilir.

Data Class

Martin Fowler'ın "Code Smell" dediği "Data Class", çoğu yazılımcı tarafından, "Code Smell" olarak kabul edilmiyor. Data Transfer Objects, Entity Objects vs. gibi birçok kullanımı var ve bunlar kaçınılmaz. Peki kim haklı?

Dead Code

Problem

Bir değişken, parametre, alan, metot veya sınıfın artık kullanılmamasıdır (genellikle artık eskimiş olduğundan).

Sebep

Yazılımın gereksinimleri değiştiğinde veya düzeltmeler yapıldığında, eski kodu temizlemek için hiç kimse zaman harcamak istemez. Bu tür bir kod karmaşık kod bloklarında da bulunabilir.

Çözüm

Ölü kodu bulmanın en hızlı yolu iyi bir IDE kullanmaktır. Çözmek ise basit: sil.

Sonuç

  • Kod uzunluğu azalır.
  • Kolay bakım.

Speculative Generality

Problem

Kullanılmayan bir sınıf, metot, alan veya parametrenin olmaması.

Sebep

Bazen kod, asla uygulanamayacak olan gelecekteki özellikleri desteklemek için yazılır. Sonuç olarak, kodun anlaşılması ve bakımı zorlaşır.

Çözüm

  • Kullanılmayan soyut sınıfları kaldırmak için: Collapse Hierarchy.
  • Gereksiz fonksiyonelliklerin başka bir sınıfa devredilmesi engellemek için: Inline Class.
  • Kullanılmayan metotlardan kurtulmak için: Inline Method.
  • Gereksiz parametreli metotlar için: Remove Parameter.
  • Kullanılmayan alanlar kolayca silinebilir.

Sonuç

  • Temiz kod.
  • Daha kolay bakım.

Ne zaman göz ardı edilebilir?

  • Eğer bir framework geliştiriyorsanız, framework'ün kendisinin kullanmadığı ama kullanıcılarının kullanabileceği bir işlev için göz ardı edilebilir.
  • Bazı birim testler, sınıf hakkında bilgileri kullanamk için ekstra alanlara ihtiyaç duyabilir. Bundan dolayı, gereksiz kod bloklarını silerken, birim testlerin bu kod bloklarını kullanıp kullanmadığından emin olun.

Feature Envy

Problem

Bir metodun, başka bir sınıfın verisine, kendisindeki veriden daha fazla erişmesi.

Sebep

Alanlar veri sınıfına taşınırken oluşur. Bu durumda, veriyle işlem yapan kodları da bu sınıfa taşımak isteyebilirsiniz.

Çözüm

Genelde veri ve bu veriyi kullanan kod blokları birlikte değişir. Bundan dolayı hepsini aynı yerde tutmak gerekir.

  • Eğer metotlar taşınacaksa: Move Method.
  • Bir metodun yalnızca bir kısmı başka bir nesnenin verilerine erişiyorsa: Extract Method.
  • Bir yöntem diğer birkaç sınıftan fonksiyonlar kullanıyorsa, ilk önce hangi sınıfların kullanılan veriyi içerdiğini belirleyin. Ardından metodu bu sınıfa diğer verilerle birlikte taşıyın. Alternatif olarak, metot küçük parçalara ayrılıp, farklı sınıflar içinde farklı metotlar olarak yer alabilirler: Extract Method.

Sonuç

  • Daha az kod tekrarı (veri işleme kodu merkezi bir yere yerleştirilirse).
  • Daha iyi kod organizasyonu (veri işleme metotlarıyla veri aynı yerde olursa).

Ne zaman göz ardı edilebilir?

Bazen davranış bilerek verileri tutan sınıftan ayrı tutulur. Bunun genel avantajı, davranışı dinamik olarak değiştirme yeteneğidir.

Inappropriate Intimacy

Problem

Bir sınıf, başka bir sınıfın "internal" alanlarını ve metotlarını kullanır. Sınıflar ne kadar birbirinden bağımsız olursa, yeniden kullanımı ve bakımı kolaylaşır.

Sebep

Kodların parça parça taşınması sırasında veya yanlış tasarımdan kaynaklanabilir.

Çözüm

Sonuç

  • Kod organizasyonunu geliştirir.
  • Bakımı ve kodun yeniden kullanımını kolaylaştırır.

Message Chains

Problem

Kodda $a->b()->c()->d() gibi bir dizi çağrı görürsünüz. Bu zincirler, sınıfların birbirlerine aşırı bağlı olmasına sebep olur. Bir sınıfta yapılan değişiklikler, diğerlerini de etkiler.

Sebep

Bir istemci bir nesne talep ettiğinde, talep edilen nesne başka bir tane daha ister ve bir mesaj zinciri oluşur.

Çözüm

  • Bir mesaj zincirini silmek için: Hide Delegate.
  • Bazen son nesnenin neden kullanıldığını düşünmek daha iyidir. Belki de bunu zincirin en önüne taşımak daha mantıklı hale gelecektir: Extract Method ve Move Method.

Sonuç

  • Bir zincirin sınıfları arasındaki bağımlılığı azaltır.
  • Şişirilmiş kodun miktarını azaltır.

Ne zaman göz ardı edilebilir?

Aşırı agresif sınıf gizleme, işlevselliğin gerçekte nerede olduğunu görmenin zor olduğu kodlara neden olabilir. Aksi halde başka bir sıkıntı oluşabilir: Middle Man.

Middle Man

Problem

Bir sınıfın tek işi, tüm işleri başka sınıflara yaptırmak.

Sebep

"Message Chains" den kurtulmak için aşırı derecede kod başka sınıflara taşındığında bu durum oluşabilir. Diğer bir sebep de, bir sınıfın kodları parça parça başka sınıflara taşındığında ortaya çıkar. İçi boşalan bir sınıf, içi boş bir kabuk gibi kalır.

Çözüm

Bir sınıf içindeki bir çok metot başka sınıflara alınıyorsa: Remove Middle Man.

Sonuç

Daha az kod.

Ne zaman göz ardı edilebilir?

  • Sınıflar arası bağımlılıkları önlemek için Middle Man eklenmiş olabilir.
  • Bazı tasarım desenleri bilerek Middle Man yaratır.

Incomplete Library Class

Problem

Er ya da geç, kütüphaneler kullanıcı ihtiyaçlarını karşılamayı durdurur. Tek çözüm ise kütüphaneyi değiştirmek ama kütüphanenin sadece okunabilir (read-only) olması, kütüphanenin değiştirilmesini imkansız hale getirir.

Sebep

Kütüphanenin yazarı, ihtiyaç duyduğunuz özellikleri sağlamadığında ya da geliştirmeyi reddettiğinde ortaya çıkar.

Çözüm

Sonuç

Kod çoğaltmasını azaltır (sıfırdan kendi kütüphanenizi oluşturmak yerine, hala mevcut olandan birisini kullanabilirsiniz).

Ne zaman göz ardı edilebilir?

Bir kütüphaneyi genişletmek, eğer kütüphanedeki değişiklikler koddaki değişiklikleri içeriyorsa ek iş üretebilir.

REFACTORING TEKNİKLERİ

Extract Method

Problem

Gruplanabilecek kod bloklarının olması.

C#
public class ExtractMethodBad
{
    public void DoSomeThing()
    {
        // diğer kod blokları...

        // kullanıcı bilgilerini ekrana bas
        Console.WriteLine("Kullanıcı adı: ali_veli");
        Console.WriteLine("E-posta: ali_veli@mail.com");
    }
}
Go
package main

func DoSomeThing() {
    // diğer kod blokları...

    // kullanıcı bilgilerini ekrana bas
    fmt.Println("Kullanıcı adı: ali_veli")
    fmt.Println("E-posta: ali_veli@mail.com")
}

Çözüm

Bu kodu ayrı bir yeni metoda taşıyın ve eski kodun yerine bu metodu çağırın.

C#
public class ExtractMethodGood
{
    public void DoSomeThing()
    {
        // diğer kod blokları...

        WriteUserInformationToConsole();
    }

    // kullanıcı bilgilerini ekrana bas
    private static void WriteUserInformationToConsole()
    {
        Console.WriteLine("Kullanıcı adı: ali_veli");
        Console.WriteLine("E-posta: ali_veli@mail.com");
    }
}
Go
package main

func DoSomeThing() {
    // diğer kod blokları...

    writeUserInformationToConsole()
}

// kullanıcı bilgilerini ekrana bas
func writeUserInformationToConsole() {
    fmt.Println("Kullanıcı adı: ali_veli")
    fmt.Println("E-posta: ali_veli@mail.com")
}

Neden?

  • Bir metodda ne kadar çok satır bulunursa, metodun ne yaptığını bulmak o kadar zor olur.
  • Gruplanan kodlar, ihtiyaç halinde başka yerden de çağrılabilir.
  • Sonraki başka bir refactoring tekniği için de bir adım olabilir.

Faydaları

  • Daha okunabilir kod. Metot ismi içindeki, gruplanmış kod satırlarının ne yaptığına dair fikir verir.
  • Daha az kod tekrarı. Kodun yeniden kullanılabilirliği artar. Tüm satırları tekrar yazmaktansa, metot çağrısı yapılır.
  • Bağımsız kod bölümlerini birbirinden izole eder, bu da daha az hata demektir. Çünkü kod bloğunun bakımı tamamen kendi sınırları içinde yapılır.

Inline Method

Problem

Bir metodun gövdesinin, metodun kendisinden daha açık olması.

C#
public class InlineMethodBad
{
    public int GetMultiplier(int number)
    {
        return IfNumberPositive(number) ? 1 : -1;
    }

    // bu metoda gerek yok
    private static bool IfNumberPositive(int number)
    {
        return number >= 0;
    }
}
Go
package main

func GetMultiplier(number int) int {
    if ifNumberPositive(number) {
        return 1
    } else {
        return -1
    }
}

// bu metoda gerek yok
func ifNumberPositive(number int) bool {
    return number >= 0
}

Çözüm

Metot çağrısını, metodun içeriğiyle değiştirin ve metodun kendisini silin.

C#
public class InlineMethodGood
{
    public int GetMultiplier(int number)
    {
        return number >= 0 ? 1 : -1;
    }
}
Go

Go Playground ile çalıştır

package main

func GetMultiplier(number int) int {
    if number >= 0 {
        return 1
    } else {
        return -1
    }
}

Neden?

Bir metot basitçe başka bir metodu çağırır ve bunda aslında bir problem yoktur. Problem, bu şekilde gereksiz metotların artmasıdır. Böyle çok fazla metot olunca, kafa karıştırıcı kodlar ortaya çıkar.

Faydaları

Gereksiz metotların sayısını en aza indirerek, kodu daha basit hale getiririz.

Extract Variable

Problem

Anlaşılması zor koşulların/ifadelerin olması.

C#
public class ExtractVariableBad
{
    public double GetTotalPrice()
    {
        var order = new Order();// get order

        return order.Quantity * order.ItemPrice -
               Math.Max(0, order.Quantity - 500) * order.ItemPrice * 0.05 +
               Math.Min(order.Quantity * order.ItemPrice * 0.1, 100);
    }
}
Go
package main

func GetTotalPrice() float64 {
    var order = Order{} // get order

    return order.Quantity * order.ItemPrice -
           math.Max(0, order.Quantity - 500) * order.ItemPrice * 0.05 +
           math.Min(order.Quantity * order.ItemPrice * 0.1, 100)
}

Çözüm

İfadenin/koşulların veya bölümlerinin sonucunu kendi kendini açıklayıcı olan ayrı değişkenlere taşıyın.

C#
public class ExtractVariableGood
{
    public double GetTotalPrice()
    {
        var order = new Order();// get order

        var basePrice = order.Quantity * order.ItemPrice;
        var quantityDiscount = Math.Max(0, order.Quantity - 500) * order.ItemPrice * 0.05;
        var shipping = Math.Min(basePrice * 0.1, 100);

        return basePrice - quantityDiscount + shipping;
    }
}
Go

Go Playground ile çalıştır

package main

func GetTotalPrice() float64 {
    var order = Order{} // get order

    var basePrice = order.Quantity * order.ItemPrice
    var quantityDiscount = math.Max(0, order.Quantity - 500) * order.ItemPrice * 0.05
    var shipping = math.Min(basePrice * 0.1, 100)

    return basePrice - quantityDiscount + shipping
}

Neden?

Kod içerisindeki uzun ifadeler kodun anlaşılmasını zorlaştırır. Kodu karmaşıklaştırır ve gereksiz uzatır. Kodu daha anlaşılır, daha kısa yapmak ve Extract Metot için bir adım oluşturmak.

Faydaları

Daha okunabilir ve anlaşılabilir kod. İfadenin ne anlama geldiğini ismi ile anlatan değişkenler.

Dezavantajları

Çok fazla değişken oluşmasına sebep olabilir. Ama kodun daha okunabilir olması bu yan etkiyi dengeler.

Inline Temp


KAYNAKLAR

NOT: Yararlanılan kaynaklar sürekli eklenecek. Bu döküman anlatım tarzı olarak https://refactoring.guru/ sitesindekine benzer bir yapı kullanıyor. Ana kaynak olarak bu siteden yararlanılıyor. Bu sitenin sahibi Alexander Shvets, içeriğin üzerine bina ettiği başka bir içeriği paralı olarak sattığı için, bedava olan kısmın birebir çevirisinin MIT lisans altında GitHub da olmasını istemiyor. Dolayısıyla bu dökümanın içeriği olabildiğince özgün, araştırılmış, tecrübe ile desteklenmiş, farklı kaynaklardan düzenlenmiş içeriklerden oluşmaktadır.

About

Türkçe refactoring kılavuzu

License:MIT License