Category Archives: Refactoring

Kataların Anası

Java ve diğer dillerde kullanılan switch komutu OCP tasarım şablonuna ters düşmekle beraber, kodun bakımı ve geliştirilmesini zorlaştırmaktadır. Martin Fowler Refactoring isimli kitabında yer alan Video Store örneğinde “Replace Conditional with Polymorphism” refactoring metodu ile switch komutunun nasıl yok edilebileceğini göstermektedir. Robert C. Martin’in CleanCoders.com sayfasında Video Store örneğini daha geniş çaplı yeniden yapılandıran bir ekran gösterimi (screencast) mevcuttur. Bu iki örnekten esinlenerek, sizler için aşağıdaki ekran gösterimini oluşturdum. Bana soracak olursanız bu tüm kataların anasıdır, çünkü içinde günlük iş hayatında bir programcının kullanalabileceği bilumum yeniden yapılandırma metotları kullanılmaktadır. Bu kata benim şahsen her gün ya da gün aşırı yaptığım favori katalardandır.

Video Store sınıflarını buradan edinebilirsiniz.

Herkesi böyle kata ekran gösterimleri hazırlamaya davet ediyorum. Bu şekilde hem birbirimizden öğrenmiş oluruz, hem de kolektif kata hafızamızı geliştiririz.

EOF (End Of Fun)
Özcan Acar

Parametrik Konstruktör (Parameterize Constructor)

Bir sınıf konstruktörü bünyesinde oluşturulan nesne, bu sınıfın test edilmesini zorlaştırır. Bunun bir örneğini EmployeeManager sınıfında görmekteyiz. Sınıf değişkeni olan dao konstruktör bünyesinde oluşturulmaktadır.

[source language=’java’]
package com.kurumsaljava.refactoring.parameterizeConstructor;

public class EmployeeManager {

private EmployeeDao dao;

public EmployeeManager() {
this.dao = new EmployeeDao();
}
}
[/source]

EmployeeManager sınıfının test edilebilmesi için EmployeeDao‘nun kullanılması zorunludur. EmployeeDao burada dolaylı bir bağımlılık teşkil etmektedir. Büyük bir ihtimalle EmployeeDao sınıfı çalışır durumda olan bir veri tabanına ihtiyaç duymaktadır. Bu durum EmployeeManager sınıfının test edilebilirliğini zora sokmaktadır.

[source language=’java’]
package com.kurumsaljava.refactoring.parameterizeConstructor;

public class EmployeeManagerTest {

public void testEmployeeManager()
{
EmployeeManager manager = new EmployeeManager();
Assert.assertTrue(manager.pay() == 0);
}
}

[/source]

Bir parametrik konstruktör oluşturarak bu sorunu ortadan kaldırabiliriz.

[source language=’java’]
package com.kurumsaljava.refactoring.parameterizeConstructor;

public class EmployeeManager {

private EmployeeDao dao;

public EmployeeManager(final EmployeeDao myDao) {
this.dao = myDao;
}

public int pay() {
return 0;
}
}
[/source]

EmployeeManager sınıfının kontruktörünü EmployeeDao sınıfından bir parametre ile genişletiyoruz. Bu şekilde ihtiyaç duyduğumuz EmployeeDao sınıfından bir nesneyi dışardan enjekte edebiliriz. EmployeeDao sınıfının bir interface sınıfı olduğunu düşünürsek, testlerimiz bünyesinde kullanılmak üzere FakeEmployeeDaoImpl ismini taşıyan ve veri tabanına bağımlılığı bulunmayan yeni bir implementasyon oluşturabiliriz.

[source language=’java’]
package com.kurumsaljava.refactoring.parameterizeConstructor;

public class EmployeeTest {

public void testEmployeeManager()
{
EmployeeDao dao = new FakeEmployeeDaoImpl();
EmployeeManager manager = new EmployeeManager(dao);
Assert.assertTrue(manager.pay() == 0);
}
}
[/source]


EOF (End Of Fun)
Özcan Acar

Yeni Sınıf Oluşturma (Extract Class)

Bir sınıfın yüzlerce ya da binlerce satırdan oluşmasının ana sebeplerinden birisi bu sınıfa birden fazla sorumluluğun yüklenmiş olmasıdır. Single Repsonsiblity (SRP) prensibinden de bildigimiz gibi her sınıfın sadece ve sadece bir sorumluluk alanı olmalıdır yani sınıf sadece bir iş yapmalıdır ve bu işlemi iyi yapmalıdır. Aşağıda yer alan Order sınıfı SRP ile uyumlu değildir. Yeni Sınıf Oluşturma (Extract Class) refactoring metodunu kullanarak bu sınıfı SRP’ye uygun hale getirebiliriz.

[source language=’java’]
package com.kurumsaljava.refactoring.extractClass;

public class Order {

private double price;

// siparis verilen ürün
private String productName;

// siparis veren müsteri bilgileri
private String customerName;
private String customerFirstnam;
private String customerBirthdate;

// müsteri adresi
private String customerStreet;
private String customerHouseNumber;
private String customerZip;
private String customerTown;
private String customerCountry;
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public String getCustomerName() {
return customerName;
}
public void setCustomerName(String customerName) {
this.customerName = customerName;
}
public String getCustomerFirstnam() {
return customerFirstnam;
}
public void setCustomerFirstnam(String customerFirstnam) {
this.customerFirstnam = customerFirstnam;
}
public String getCustomerBirthdate() {
return customerBirthdate;
}
public void setCustomerBirthdate(String customerBirthdate) {
this.customerBirthdate = customerBirthdate;
}
public String getCustomerStreet() {
return customerStreet;
}
public void setCustomerStreet(String customerStreet) {
this.customerStreet = customerStreet;
}
public String getCustomerHouseNumber() {
return customerHouseNumber;
}
public void setCustomerHouseNumber(String customerHouseNumber) {
this.customerHouseNumber = customerHouseNumber;
}
public String getCustomerZip() {
return customerZip;
}
public void setCustomerZip(String customerZip) {
this.customerZip = customerZip;
}
public String getCustomerTown() {
return customerTown;
}
public void setCustomerTown(String customerTown) {
this.customerTown = customerTown;
}
public String getCustomerCountry() {
return customerCountry;
}
public void setCustomerCountry(String customerCountry) {
this.customerCountry = customerCountry;
}
}
[/source]

Yeni Sınıf Oluşturma (Extract Class) refactoring metodunu uygulayarak ürün, müşteri ve müşteri adresi bilgilerini kendi sınıflarına taşıyabiliriz. Bu işlemin ardından Order sınıfı aşağıdaki yapıya sahip olacaktır:

[source language=’java’]

package com.kurumsaljava.refactoring.extractClass;

public class OrderTemp {

private Product product = new Product();

private Customer customer = new Customer();

private CustomerAddress address = new CustomerAddress();

public double getPrice() {
return product.getPrice();
}
public void setPrice(double price) {
this.product.setPrice(price);
}
public String getProductName() {
return product.getProductName();
}
public void setProductName(String productName) {
this.product.setProductName(productName);
}
public String getCustomerName() {
return customer.getCustomerName();
}
public void setCustomerName(String customerName) {
this.customer.setCustomerName(customerName);
}
public String getCustomerFirstnam() {
return customer.getCustomerFirstnam();
}
public void setCustomerFirstnam(String customerFirstnam) {
this.customer.setCustomerFirstnam(customerFirstnam);
}
public String getCustomerBirthdate() {
return customer.getCustomerBirthdate();
}
public void setCustomerBirthdate(String customerBirthdate) {
this.customer.setCustomerBirthdate(customerBirthdate);
}
public String getCustomerStreet() {
return address.getCustomerStreet();
}
public void setCustomerStreet(String customerStreet) {
this.address.setCustomerStreet(customerStreet);
}
public String getCustomerHouseNumber() {
return address.getCustomerHouseNumber();
}
public void setCustomerHouseNumber(String customerHouseNumber) {
this.address.setCustomerHouseNumber(customerHouseNumber);
}
public String getCustomerZip() {
return address.getCustomerZip();
}
public void setCustomerZip(String customerZip) {
this.address.setCustomerZip(customerZip);
}
public String getCustomerTown() {
return address.getCustomerTown();
}
public void setCustomerTown(String customerTown) {
this.address.setCustomerTown(customerTown);
}
public String getCustomerCountry() {
return address.getCustomerCountry();
}
public void setCustomerCountry(String customerCountry) {
this.address.setCustomerCountry(customerCountry);
}
}
[/source]

Değişik sorumluluk alanlarını ifade eden sınıf değişkenleri yerine sorumluluk alanını kapsayan sınıf tipinde değişkenler kullandık. Böylece her bir sorumluluk alanını yeni bir sınıf ile izole etmiş olduk. Order bünyesinde yer alan metotlar delegasyon usulü ile bu yeni degişkenleri kullanabilirler. Eğer bunu istemiyorsak Order bünyesinde bulunan eski sınıf değişkenleri ile ilişkili tüm metotları kaldırarak, Order sınıfını kullanan diğer sınıfların oluşturdugumuz yeni sınıfları kullanmalarını sağlayabiliriz.


EOF (End Of Fun)
Özcan Acar

Metodu Metot Nesnesine Dönüştürme (Replace Method with Method Object)

Metodu Metot Nesnesine Dönüştürme (Replace Method with Method Object) refactoring metodu aşağıdaki durumlarda kullanılabilir:

  • Çok sayıda lokal değişken Yeni Metot Oluşturma (Extract Method) refactoring metodunun kullanımını engelliyor.
  • Uzun bir metot bünyesinde tanımlanan lokal değişkenlerin kapsama alanı (scope) geniş yani lokal değişkenler metodun sonuna kadar kullanılıyor.
  • Metot SRP tasarım prensibi ile uyumlu degil yani refactor etmek istediğimiz metot, bünyesinde bulunduğu sınıfa yeni bir sorumluluk yüklüyor.

Metodu Metot Nesnesine Dönüştürme (Replace Method with Method Object) refactoring metodu ile yeni bir sınıf oluşturulur ve uzun metod bu sınıfa taşınır. Metot bünyesinde tanımlanmış olan lokal değişkenler sınıf değişkenleri olarak değiştirilir. Metodun yer aldığı eski sınıf bünyesinde yeni oluşturulan sınıftan bir nesne oluşturularak, yeni sınıf bünyesindeki metoda delege edilir.

Aşağıda yer alan örnekte calculatePrice metodu Order sınıfına yeni bir sorumluluk yüklediği için Metodu Metot Nesnesine Dönüştürme (Replace Method with Method Object) refactoring metodunu kullanarak PriceCalculator isminde yeni bir sınıf oluşturuyor ve calculatePrice metodunu bu sınıfa taşıyoruz.

[source language=”java”]
package com.kurumsaljava.refactoring.replaceMethodwithMethodObject;

public class Order {

private double price;
private String locale;

public static Double calculatePrice(final Order order) {
Double result = new Double(0);
final String locale = order.getLocale();
final double price = order.getPrice();
double taxRate = 0;
if (locale != null && locale.equals(“de_DE”)) {
taxRate = 19.00;
final Double tax = Double.valueOf((price / 100) * taxRate);
result = Double.valueOf(tax.doubleValue() + price);
} else if (locale != null && locale.equals(“tr_TR”)) {
taxRate = 18.00;
final Double tax = Double.valueOf((price / 100) * taxRate);
result = Double.valueOf(tax.doubleValue() + price);
}
return result;
}

public double getPrice() {
return price;
}

public String getLocale() {
return locale;
}
}
[/source]

[source language=”java”]
package com.kurumsaljava.refactoring.replaceMethodwithMethodObject;

public class PriceCalculator {

public Double calculatePrice(final Order order) {
Double result = new Double(0);
final String locale = order.getLocale();
final double price = order.getPrice();
double taxRate = 0;
if (locale != null && locale.equals(“de_DE”)) {
taxRate = 19.00;
final Double tax = Double.valueOf((price / 100) * taxRate);
result = Double.valueOf(tax.doubleValue() + price);
} else if (locale != null && locale.equals(“tr_TR”)) {
taxRate = 18.00;
final Double tax = Double.valueOf((price / 100) * taxRate);
result = Double.valueOf(tax.doubleValue() + price);
}
return result;
}
}
[/source]

Bu işlemin ardında Order sınıfını aşağıdaki şekilde değiştiriyoruz.

[source language=”java”]
package com.kurumsaljava.refactoring.replaceMethodwithMethodObject;

public class Order {

private double price;
private String locale;

public static Double calculatePrice(final Order order) {
return new PriceCalculator().calculatePrice(order);
}

public double getPrice() {
return price;
}

public String getLocale() {
return locale;
}
}
[/source]

Bu değişikliğin ardından Order sınıfında bulunan fiyat hesaplama metodunu PriceCalculator sınıfına taşımış olduk. Order sınıfı fiyat hesaplama işlemi için yeni oluşturduğumuz PriceCalculator sınıfını kullanır hale geldi.


EOF (End Of Fun)
Özcan Acar

Geçici Değişkenlerin Sorguya Dönüştürülmesi (Replace Temp with Query)

Bir metot bünyesinde bir işlemin sonucu geçici bir değişkende saklanabilir. Aşağıda yer alan kod örneğinde basePrice geçici bir değişkendir ve ihtiva ettiği değer fiyat * adet şeklinde hesaplanmaktadır. Replace Temp with Query refactoring metodunu kullanılarak geçici değişkenin değerini elde etmek için kullanılan yapı yeni bir metot bünyesinde taşınır. Geçici değişkenin kullanıldığı diğer alanlar metot ismiyle değiştirilir. Bu şekilde yapılan işlemin başka metotlar bünyesinde kullanılması sağlanır.

[source language=”java”]
public double calculatePrice(final double price, final int itemCount)
{
double basePrice = price * itemCount;
double tax = ( basePrice / 100 ) * 18.00;
double totalPrice = basePrice + tax;
return totalPrice;
}
[/source]

Refactoring işleminin ardından oluşan yeni yapı aşağıdaki şekildedir.

[source language=”java”]
package com.kurumsaljava.refactoring.replaceTempwithQuery;

public class ReplaceTempwithQueryRefactored {

private double price;
private int itemCount;

public ReplaceTempwithQueryRefactored(final double pPrice,
final int pItemCount) {
this.price = pPrice;
this.itemCount = pItemCount;
}

public double calculatePrice() {
double tax = (getBasePrice() / 100) * 18.00;
return getBasePrice() + tax;
}

private double getBasePrice() {
return price * itemCount;
}
}
[/source]

getBasePrice isminde, başka bir metot bünyesinde tekrar kullanılabilir yapıda yeni bir metot oluşturmus olduk. getBasePrice sorgu (query) yaptığımız bir metotdur. Belli bir değeri elde etmek için bu metodu sorgulamaktayız. basePrice isimli geçici değişkeni ortadan kaldırmak için getBasePrice metodunu kullandık yani geçici değişkeni bir sorguya dönüştürmüş olduk. Bu sebepten dolayı Replace Temp with Query‘yi Geçici Değişkenlerin Sorguya Dönüştürülmesi olarak tercüme ettim.

calculatePrice metodunu tekrar gözden geçirdiğimiz zaman, katma değer verginin hesaplandığı görmekteyiz. Eğer daha önce basePrice geçici değişkenini bir sorguya dönüştürmemiş olsaydık, katma değer vergisinin hesaplandığı bölümü refactor etmemiz kolay olmayacaktı. Ama bu şekilde katma değer vergisinin hesaplandığı kod kısmını aşağıdaki şekilde refactor edebiliriz.

[source language=”java”]
package com.kurumsaljava.refactoring.replaceTempwithQuery;

public class ReplaceTempwithQueryRefactored {

private static final double TAX_RATE = 18.00;
private double price;
private int itemCount;

public ReplaceTempwithQueryRefactored(final double pPrice,
final int pItemCount) {
this.price = pPrice;
this.itemCount = pItemCount;
}

public double calculatePrice() {
double tax = calculateTax();
return getBasePrice() + tax;
}

private double calculateTax() {
return (getBasePrice() / 100) * TAX_RATE;
}

private double getBasePrice() {
return price * itemCount;
}
}
[/source]


EOF (End Of File)
Özcan Acar

Parametrele Değer Atamasının Kaldırılması (Remove Assignments to Parameters)

Metot imzasında yer alan parametrelere metot bünyesinde değer atanmamalıdır. Bu gibi atamalar istenmeyen sonuçlar doğurabilir. Remove Assignments to Parameters refactoring metodu kullanılarak bu tür parametre değer atamaları geçici degişkenlere yapılmalıdır.

[source language=”java”]
private void execute(int value) {
if (value == 10)
value = 1;
doSometthink(value);
}
[/source]

Yukarda yer alan metot Remove Assignments to Parameters uygulandığı taktirde aşağıdaki yapıya sahip olacaktır. Metot bünyesinde result isminde yeni geçici bir değişken tanımlanmış ve böylece parametrenin taşıdığı değer metot gövdesinde korunmuştur.

[source language=”java”]
private void execute(int value) {
int result = 0;
if (value == 10)
result = 1;
doSometthink(result);
}
[/source]

final kelimesi kullanılarak metot bünyesinde parametrelere değer atanması otomatik olarak engellenebilir.

[source language=”java”]
private void execute(final int value) {
if (value == 10)
value = 1;
doSometthink(value);
}
[/source]

Yukarda yer alan value=1 şeklindeki atama, value parametresi final olduğu için aşağıda yer alan hataya sebep olacaktır:

The final local variable value cannot be assigned. It must be blank and not using a compound assignment


EOF (End Of Fun)
Özcan Acar

Yeni Metot Oluşturma (Extract Method)

Uzun metotları daha okunabilir ve kompak hale getirmek için Extract Method refactoring metodunu kullanabiliriz.

[source language=”java”]
private Double calculatePrice(double price, String locale) {
Double result;
double taxRate = 0;
if (isCountryGermany(locale)) {
taxRate = TAX_RAT_GERMANY;
} else if (isCountryTurkishRepublic(locale)) {
taxRate = TAX_RAT_TURKISH_REPUBLIC;
}
Double tax = Double.valueOf( (price / 100) * taxRate);
result = Double.valueOf(tax.doubleValue() + price);
return result;
}
[/source]

calculatePrice metodu bünyesinde ülkeye bağlı olarak katma deger vergisi oranı (taxRate) tespit edilmektedir. Bu işlemi aşağıdaki şekilde başka bir metot bünyesine alarak, calculatePrice metodunu küçültebiliriz.

[source language=”java”]
private Double calculatePrice(double price, String locale) {
Double result;
double taxRate = 0;
taxRate = getTaxRateBasedOnCountry(locale);
Double tax = Double.valueOf( (price / 100) * taxRate);
result = Double.valueOf(tax.doubleValue() + price);
return result;
}

private double getTaxRateBasedOnCountry(String locale) {
double taxRate = 0.0;
if (isCountryGermany(locale)) {
taxRate = TAX_RAT_GERMANY;
} else if (isCountryTurkishRepublic(locale)) {
taxRate = TAX_RAT_TURKISH_REPUBLIC;
}
return taxRate;
}
[/source]

Bu refactoring metodunu kullanırken seçtiğimiz metot isimlerinin, işlemin nasıl yapıldığını değil, hangi işlemin yapıldığını yansıtmasına dikkat etmemiz gerekmektedir. Bu kodun okunmasını kolaylaştıracaktır. Uzunda olsa yapılan işlemleri tanımlayan metot isimleri kullanmaktan kaçınmamamız gerekmektedir.

Yeni oluşturulan metodun küçük tutulmasında fayda vardır. Bu, yeni metodun başka bir metot bünyesinde tekrar kullanım şansını artıracaktır.

Eğer uzun bir metodun bu şekilde parçalarına bölünmesi okunurluluğu ve kodun anlaşılma seviyesini artırıyorsa Extract Method refactoring metodu kullanılmalıdır.

Bu refactoring işleminin ardından bile calculatePrice metodunu daha da kompak hale getirme potansiyeli vardır. calculatePrice metodunu aşağıdaki şekilde tekrar refactor etmeye ne dersiniz?

[source language=”java”]
private Double calculatePrice(double price, String locale) {
double taxRate = getTaxRateBasedOnCountry(locale);
return getCalculatedPriceWithTax(price, taxRate);
}

private Double getCalculatedPriceWithTax(double price, double taxRate) {
Double result;
Double tax = Double.valueOf( (price / 100) * taxRate);
result = Double.valueOf(tax.doubleValue() + price);
return result;
}

private double getTaxRateBasedOnCountry(String locale) {
double taxRate = 0.0;
if (isCountryGermany(locale)) {
taxRate = TAX_RAT_GERMANY;
} else if (isCountryTurkishRepublic(locale)) {
taxRate = TAX_RAT_TURKISH_REPUBLIC;
}
return taxRate;
}
[/source]

Şimdi calculatePrice metodunun eski ve yeni halini bir kıyaslayalım. Hangi metot size ilk bakışta ne yaptığını daha kolay anlatıyor?

Eski hali:
[source language=”java”]
private Double calculatePrice(double price, String locale) {
Double result;
double taxRate = 0;
if (isCountryGermany(locale)) {
taxRate = TAX_RAT_GERMANY;
} else if (isCountryTurkishRepublic(locale)) {
taxRate = TAX_RAT_TURKISH_REPUBLIC;
}
Double tax = Double.valueOf( (price / 100) * taxRate);
result = Double.valueOf(tax.doubleValue() + price);
return result;
}

[/source]

Yeni hali:
[source language=”java”]
private Double calculatePrice(double price, String locale) {
double taxRate = getTaxRateBasedOnCountry(locale);
return getCalculatedPriceWithTax(price, taxRate);
}
[/source]

calculatePrice metodunda gördüğümüz gibi oluşturdugumuz metotlar 4-5, en fazla 6-7 satırı geçmemelidir. Bu kurala uyduğumuz taktirde aşağıda yer aldığı gibi, okunması, bakımı ve geliştirilmesi çok kolay sınıflar oluşur.

[source language=”java”]
package com.kurumsaljava.refactoring.extractmethod;

public class TaxCalculator {
private static final String LOCALE_DE = “de_DE”;
private static final String LOCALE_TR = “tr_TR”;
private static final double TAX_RAT_GERMANY = 19.00;
private static final double TAX_RAT_TURKISH_REPUBLIC = 18.00;
private static final Double NULL_VALUE_DOUBLE_OBJECT = new Double(0);

public Double calculate(String locale, double price) {
return calculateBasedOnLocale(locale, price);
}

private Double calculateBasedOnLocale(String locale, double price) {
if (isCountryGermany(locale)) {
return calculatePrice(price, locale);
} else if (isCountryTurkishRepublic(locale)) {
return calculatePrice(price, locale);
} else
return NULL_VALUE_DOUBLE_OBJECT;
}

private Double calculatePrice(double price, String locale) {
double taxRate = getTaxRateBasedOnCountry(locale);
return getCalculatedPriceWithTax(price, taxRate);
}

private Double getCalculatedPriceWithTax(double price, double taxRate) {
Double tax = Double.valueOf( (price / 100) * taxRate);
return Double.valueOf(tax.doubleValue() + price);
}

private double getTaxRateBasedOnCountry(String locale) {
if (isCountryGermany(locale)) {
return TAX_RAT_GERMANY;
} else if (isCountryTurkishRepublic(locale)) {
return TAX_RAT_TURKISH_REPUBLIC;
} else
return NULL_VALUE_DOUBLE_OBJECT.doubleValue();
}

private boolean isCountryGermany(String locale) {
return locale != null && locale.equals(LOCALE_DE);
}

private boolean isCountryTurkishRepublic(String locale) {
return locale != null && locale.equals(LOCALE_TR);
}
}
[/source]

Refactoring için çıkış noktamız aşağıdaki sınıftı. Umarım Extract Method refactoring metodunun ne kadar kıymetli bir araç olduğunu gösterebilmişimdir :)

[source language=”java”]
package com.kurumsaljava.refactoring.extractmethod;

public class TaxCalculatorOld {

public Double calculate(String locale, double price) {
Double result = new Double(0);
double taxRate = 0;
if (locale != null && locale.equals(“de_DE”)) {
taxRate = 19.00;
Double tax = Double.valueOf( (price / 100) * taxRate);
result = Double.valueOf(tax.doubleValue() + price);
} else if (locale != null && locale.equals(“tr_TR”)) {
taxRate = 18.00;
Double tax = Double.valueOf( (price / 100) * taxRate);
result = Double.valueOf(tax.doubleValue() + price);
}
return result;
}
}
[/source]

Koşulları Parçalarına Ayırma (Reverse Conditional)

TaxCalculator sınıfı, uygulamanın kullanıldığı ülkeye bağlı olarak katma değer vergisini ihtiva eden fiyatı hesaplamak için kullanılmaktadır.

[source language=’java’]
package com.kurumsaljava.refactoring.reverseconditional;

public class TaxCalculator{
private static final double TAX_RAT_GERMANY = 19.00;
private static final double TAX_RAT_TURKISH_REPUBLIC = 18.00;

public Double calculate(String locale, double price){
Double result = new Double(0.0);

if(locale != null && locale.equals(“de_DE”)){
Double tax = Double.valueOf( (price / 100 ) * TAX_RAT_GERMANY);
result = Double.valueOf(tax.doubleValue() + price);
}
else if(locale != null && locale.equals(“tr_TR”)){
Double tax = Double.valueOf( (price / 100) * TAX_RAT_TURKISH_REPUBLIC);
result = Double.valueOf(tax.doubleValue() + price);
}
return result;
}
}

[/source]

calculate gibi metotlarda karşılaştığımız en büyük sorunlardan birisi if/else blokları içinde mantıksal operatörler kullanılarak bir takım koşulların tanımlanması ve bu koşullara bağımlı olarak bazı işlemlerin yapılmasıdır. Bu tür bir programlama tarzı ne yazık ki uzun ve anlaşılması zor metotların oluşmasına sebep olmaktadır. calculate metoduna baktığımızda if/else bünyesinde ne olup bittiğini belki anlamaktayız, lakın neden bunun yapıldığını anlamak her zaman mümkün olmamaktadır.

Bahsettiğim sorunları ortadan kaldırmak için Reverse Conditional refactoring metodunu kullanabiliriz.

[source language=’java’]
package com.kurumsaljava.refactoring.reverseconditional;

public class TaxCalculator {
private static final String LOCALE_DE = “de_DE”;
private static final String LOCALE_TR = “tr_TR”;
private static final double TAX_RAT_GERMANY = 19.00;
private static final double TAX_RAT_TURKISH_REPUBLIC = 18.00;

public Double calculate(String locale, double price) {
Double result = new Double(0.0);

if (isCountryGermany(locale)) {
result = calculatePrice(price, locale);
} else if (isCountryTurkishRepublic(locale)) {
result = calculatePrice(price, locale);
}
return result;
}

private boolean isCountryTurkishRepublic(String locale) {
return locale != null && locale.equals(LOCALE_TR);
}

private Double calculatePrice(double price, String locale) {
Double result;
double taxRate = 0;

if (isCountryGermany(locale)) {
taxRate = TAX_RAT_GERMANY;
} else if (isCountryTurkishRepublic(locale)) {
taxRate = TAX_RAT_TURKISH_REPUBLIC;
}

Double tax = Double.valueOf( (price / 100) * taxRate);
result = Double.valueOf(tax.doubleValue() + price);
return result;
}

private boolean isCountryGermany(String locale) {
return locale != null && locale.equals(LOCALE_DE);
}
}
[/source]

Yeni implmentasyonu gözden geçirdiğimizde calculate metodunda olup bitenlerin ne anlama geldiğini daha iyi algılayabilmekteyiz. Reverse Conditional refactoring metodunu kullanarak if/else koşullarını parçalara böldük ve yeni metotlar oluşturduk. Kullandığımız metot isimleri yapılan işlemi algılamamızı daha kolaylaştırmakta ve bu şekilde kodun okunulurluk derecesi artmaktadır.


EOF (End Of Fun)
Özcan Acar