25 Nisan 2014 Cuma

Daha Okunabilir Kod ve Yaşanabilir Dünya İçin Enum'lar

Selamlar,

Enum(Enumaration) tanımlamaları programlama dillerinde belirli değerlere karşılık gelen sabit değerlerin temsili için kullanılırlar, haftanın günleri, yönler vs gibi. Geliştiriciye sağladığı faydalar ise kod okunabilirliğini arttırması, muhtemel değer kümesinin daraltılması ve tip güvenliğini sağlaması dolayısıyla hata payını en aza indirmesi. Ben enumları java dili üzerinden ele alacağım; enum sabiltleri java'ya jdk 1.5 sürümü ile eklenmiştir, ayrıntılar için buraya tıklayınız. Daha öncesinde enum'un üstelendiği görev constant'lar ile kotarılıyordu. "Peki o zaman ne gerek vardı dile enum tipini eklemeye?" diye sorabilirsiniz, ilerleyen bölümlerde sebeplerini açıklıyor olacağım, şimdilik okunabilirlik tarafıyla devam edelim.

Geliştirme yaparken bazen program akışını yönlendiren belirli durumlar oluşur, biz de bu durumları saptar, sayısal değerler ile ilişkilendirip kodu şekillendiririz. Örneğin elimizde döküman modeli var ve farklı döküman tipleri için farklı yazdırma işlemi yapmamız gerekebilir, bu duruma aşağıdaki gibi "pratik" bir yaklaşımla çözüm getirilebilir:
public class DocumentPrinter {
    public static void print(String path, int documentType){
        if (documentType == 0){  // word
            // word dokumani yazdirma
        } else if (documentType == 1){ // pdf
            // pdf dokumani yazdirma
        } else {
           // html dokumani yazdirma
        }
    }
}

// kullanim
String path= "xx/ebook.doc";
DocumentPrinter.print(path, 0);
İster karşılandı lakin farkedeceğiniz gibi ortaya okunabilirliği zayıf bir kod çıktı:
- Döküman tip bilgisi ancak yorum satırı üzerinden anlaşılıyor;  print() metodunun ne yaptığını anlamak için gövdesine bakmak gerek
- DocumentPrinter.print() metodu çağrımında documentType parametresi için hatalı değer geçme ihtmali yüksek

Vakti zamanında üzerimdeki task'leri yetiştirmek adına benzer problemi yukarıdaki şekilde çözüp gönderdiğim kodlar reject yerdi code review'de, iş başa düşerdi :] Neyse bu noktada kodu daha okunaklı ve güvenli hale getirmek için constant(sabit) kullanımı yoluna gidilebilir;
public class DocumentPrinter {
    public static final int DOC_WORD = 0;
    public static final int DOC_PDF = 1;
    public static final int DOC_HTML = 2;

    public static void print(String path, int documentType){
        if (documentType == DOC_WORD){
            // word dokumani yazdirma
        } else if (documentType == DOC_PDF){
            // pdf dokumani yazdirma
        } else{
           // html dokumani yazdirma
        }
    }
}

// kullanim
String path= "xx/ebook.doc";
DocumentPrinter.print(path, DocumentPrinter.DOC_WORD);
DocumentPrinter sınıfına eklediğimiz "DOC_" prefix'li sabit tanımlarıyla kodun okunabilirliğini ve kullanım kolaylığını biraz daha arttırmış olduk. Fakat bu durumda DocumentPrinter.print(path, 5); gibi bir kullanım için halen derleme zamanında hata/uyarı alamamamız(no type-safety) bizim için olumsuz bir durum, tarifi imkansız bir acı :]

Gelinen durumdaki problemi aşmanın yolu malunuz print() metodunun documentType parametresi için beklenen değer kümesini daraltmak, bunu da enum sabitlerini kullanarak aşacağız. Bu sebepten  DocumentType isimli bir enum tanımlayıp mevcut kodu düzenliyoruz:
public enum DocumentType {
 WORD,
 PDF,
 HTML
}

public class DocumentPrinter {
    public static void print(String url, DocumentType type){
        if (type == DocumentType.WORD ){
            // word dokumani yazdirma
        } else if (type ==  DocumentType.PDF){
            // pdf dokumani yazdirma
        }else{
           // html dokumani yazdirma
        }
    }
}

// kullanim
String path= "xx/ebook.doc";
DocumentPrinter.print(path, DocumentType.WORD);
Enum kullanarak mevcut kodu daha okunabilir ve güvenli hale getirdik bunun yanında günümüz modern ide'lerinin enum'lar için kod tamamlama desteği sağlamasıyla kod yazmak daha zevkli hale geldi.

Enum tanımlamasının constant'a olan bir üstünlüğü de kendi name-space'ine(gruplama) sahip oluşu. Örneğin android gui'sinin temel taşı olan View sınıfının haliyle çok contstant'ı vardır, dolayısıyla bir view elemanın görünürlüğünü atarken view.setVisibility() metoduna parametre geçerken "View." yazdığımızda ide bize kod tamamlama için bir sürü alakasız constant değeri sunacaktır. Constant tanımlamalarının kullanıldığı sınıf büyüdükçe, gruplama noksanlığı sebebiyle kullanımı da daha zevksiz bir hale gelecektir.

Son olarak değinmek istediğim, karşılaşmanız muhtemel bir kullanım senaryosu var; diyelim ki bir iş uygulamasında elinizde Document entity'si var ve bu entity'nin property'lerinden(başlık, oluşturulma tarihi vs) biri de dökümanın tipini temsil ediyor olsun, yukarıdaki DocumentType gibi. Java tarafında her şey güzel de doküman verilerini veritabanından okuyor yahut  veritabanına yazıyor olsak bu durumda bizdeki DocumentType enum'unun veritabanda karşılığı ne olacak?

Bu problemi, veritabanında numerik anahtarlar üzerinden tutarlığının sağlandığını göz önünde bulundurursak en uygun çözüm, enum değerlerinin numerik değerlerle ilişkilendirilmesini sağlamak olacaktır. Yani DocumentType değerlerini numerik değerlere çevirebilmeli(veritabanına yazarken) yine tersi yöndeki çevrimi(veritabanından okurken) de desteklemeliyiz. Bu durumda DocumentType tipini aşağıdaki gibi refaktöre etmeliyiz:
public enum DocumentType {
    WORD(1), PDF(2), HTML(3);

    private final int value;

    private DocumentType(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }

    public static DocumentType parse(int value){
        // DocumentType.values() metodu değer kümesini döndürür
        for (DocumentType type : DocumentType.values()) {
            if (value == type.getValue()) {
                return type;
            }
        }
        return PDF;
    }
}
// test //
// db den oku: 2 değerinin veritabanindan cursor ile okunduğunu farz edin
DocumentType pdf = DocumentType.parse(2);
System.out.println(pdf);
// db ye yaz: val degiskenin degerinin db'ye yazılabilir
int val = pdf.getValue();
System.out.println(val);
// çıktı //
PDF
2

Process finished with exit code 0

Burada enum değerinin numerik değerle ilikisini value field'ı ile sağladık, daha sade tutmak adına enum değerlerinin tanımlanma sırasını döndüren ordinal() metodunu kullanabilirdik fakat zaman içinde enum değerlerinin sırası değişmesi vs sebebiyle kolay kırılan bir yapı oluşacaktı; üşenmedik, uzun ama sağlıklı olan yolu tercih ettik.

Geliştiriciler olarak artık yazdığımız kodun çalıştığına sevinmeyi geçip, o kodu bizden daha sonra başka birinin (belki de koda yabancıylaşmaya yetecek kadar süre geçtikten sonra kendimizin) okumak durumunda kalacağını düşünerek, ortaya çıkan ürünün sürdürülebilirliğini sağlamak adına daha temiz/sade kod yazma çalışmalıyız.

Bu blog girdisinde enum'ların kod okunabilirliğine katkısı ve geliştiricilere sağladığı kullanım kolaylıklarını incelemeye çalıştım, biraz uzattım sanırım umarım sıkılmamışsınızdır, iyi günler.

2 yorum: