Home > Java > Effective Java Madde 10: toString() Metodunu Her Zaman Geçersiz Kılın

Effective Java Madde 10: toString() Metodunu Her Zaman Geçersiz Kılın

Her ne kadar java.lang.Object bir toString() gerçekleştirimi sunsa da, geri döndürdüğü karakter dizisi genellikle sizin sınıfınızı kullanmak isteyen bir yazılımcının görmek istediği şey değildir. Bu değer sınıfın adı, ardından gelen ‘@’ karakteri ve nesnenin hash kodunun onaltılık sistemde (hexadecimal) ifadesini içermektedir, örneğin “PhoneNumber@163b91”. toString sözleşmesi, bu metottan döndürülen değerin “kolay okunup anlaşılabilen, kısa ama bilgi verici” olması gerektiğini söyler. “PhoneNumber@163b91” değerinin kısa ve okuması kolay olduğunu savunabilirsiniz ancak “(707) 867-5309” ile karşılaştırıldığında bilgi verici olmadığı açıktır. toString sözleşmesi bunların haricinde “Bu metodu bütün alt sınıfların geçersiz kılması tavsiye edilir” demektedir. Kesinlikle uyulması gereken bir tavsiye!

Her ne kadar equals (Madde 8) ve hashCode (Madde 9) sözleşmeleri kadar kritik olmasa da, güzel bir toString gerçekleştirimi sağlamak sınıfınızı çok daha kolay kullanılabilir hale getirir. toString metodu, bir nesne printf, println gibi metotlara parametre olarak geçildiğinde, karakter dizilerine eklendiğinde veya assert ile birlikte kullanıldığında otomatik olarak işletilir.

PhoneNumber sınıfı için iyi bir toString metodu tanımladıysanız, yararlı debug mesajları yazdırmak aşağıdaki gibi son derece kolay olacaktır.

System.out.println("Failed to connect: " + phoneNumber);

Sınıfınızı kullanan programcılar bunun gibi debug mesajlarını siz bir toString metodu sağlasanız da sağlamasanız da yazdıracaklardır, ancak sağlamazsanız görecekleri mesaj çok da faydalı olmayacaktır. İyi bir toString metodu tanımlamanın bir başka faydası da sınıfınız bir veri yapısı içerisinde kullanıldığında ortaya çıkar. Bir HashMap yazdırdığınızda “{Jenny=PhoneNumber@163b91}” mi yoksa “{Jenny=(707) 867-5309}” mi görmek isterdiniz?

Elverişli durumlarda toString metodu nesne içerisindeki bütün önemli alanları içeren bir karakter dizisi döndürmelidir. Yukarıdaki PhoneNumber sınıfı buna bir örnek olabilir. Eğer nesne çok fazla alan içeriyorsa veya içerdiği alanlar karakter dizisi olarak ifade edilemiyorsa, o zaman “Manhattan white pages (1487536 listings)” gibi makul bir özet karakter dizisi döndürülebilir.

toString metodunu geçersiz kılarken vermeniz gereken önemli kararlardan birisi de döndürdüğünüz karakter dizisinin formatını belirleyip belirlememektir. PhoneNumber gibi değer sınıfları için bunu yapmanız tavsiye edilir. Format belirlemenin avantajı, nesnenizi standart, net ve okunabilir bir karakter dizisi olarak ifade edebilmektir. Bu ifade şekli girdi/çıktı olarak kullanılabilir veya XML gibi dosyalar içerisinde saklanabilir. Eğer bir format tanımlarsanız, bunun yanında uyumlu bir statik fabrika metodu veya yapıcı metot tanımlamak da mantıklı olacaktır. Böylece programcılar nesnenin kendisi ve karakter dizisi ifadesi arasında kolayca geçiş yapabilirler. Bu yaklaşım BigInteger, BigDecimal gibi birçok değer sınıfı için Java kütüphanesi içerisinde kullanılmıştır.

toString için bir format belirlemenin dezavantajı ise, eğer sınıfınız yaygın olarak kullanılıyorsa belirlediğiniz formatı gelecekte değiştirme şansınızın kalmamasıdır. Programcılar sizin formatınıza uygun olarak karakter dizisi üzerinde işlem yapan kodlar yazacaklardır ve siz formatı değiştirdiğiniz zaman bu kodlar çalışmayacaktır. Bu da sınıfınızı kullanan programcıları memnun etmeyecektir. Bir format belirlemediğiniz zaman gelecek sürümlerde değişiklik yapma esnekliğini korumuş olursunuz.

Bir format belirleseniz de belirlemeseniz de, niyetinizi açık bir biçimde belirtmelisiniz. Format belirlediyseniz de net bir biçimde anlatmalısınız. Mesela Madde 9’da yazdığımız PhoneNumber sınıfı için aşağıdaki gibi bir toString olabilir:

/**
 * Telefon numarası nesnesinin karakter dizisi olarak ifadesini döndürür.
 * Bu karakter dizisi 14 karakterden oluşur ve formatı şu şekildedir
 * "(XXX) YYY-ZZZZ", burada XXX alan kodu, YYY ön ek ve ZZZZ hat numarasıdır. 
 * (Her bir büyük harf bir rakamı temsil etmektedir)
 *
 * Eğer bu telefon numarasının üç kısmından herhangi birisi o alanı  
 * dolduracak kadar büyük değilse, o zaman başına yeteri kadar 0 eklenir.
 * Mesela, hat numarası 123 ise, karakter dizisinin son 4 karakteri "0123" olacaktır. 
 * 
 * Alan kodunun ardından kapatılan parantezden sonra bir boşluk karakteri 
 * geldiğini gözden kaçırmayın.
 */
@Override public String toString() {
    return String.format("(%03d) %03d-%04d", areaCode, prefix, lineNumber);
}

Eğer bir format belirlemezseniz, aşağıdaki gibi biri dokümantasyon yapabilirsiniz:

/**
 * Bu iksirin kısa bir açıklamasını üretir. Üretilen karakter dizisi ileride değişebilir
 * aşağıda tipik bir örnek verilmektedir:
 *
 * "[İksir No: #9: tür=aşk, koku=neft yağı, görünüm=çini mürekkebi]" */
@Override 
public String toString() { 
    ... 
}

Bu açıklamayı okuduktan sonra bu formatı baz alarak kod yazan programcılar, format değiştiği zaman kendilerinden başka suçlayacak kimseyi bulamayacaklardır.

Format belirleseniz de belirlemeseniz de, toString tarafından döndürülen değerde bulunan bütün alanlara erişim sağlayın. Örneğin, PhoneNumber sınıfı alan kodu, ön ek ve hat numarası alanlarına erişim metotları sağlamalıdır. Eğer bunu yapmazsanız, sınıfınızı kullanan programcıları toString metodunun döndürdüğü karakter dizisi ile çalışmaya zorlamış olursunuz. Bu durum da performans kaybına ve gereksiz kod yazılmasına yol açar, hatalı kod ihtimalini artırır ve format değişikliği olduğunda kolayca bozulan, kırılgan yazılımlara sebebiyet verir. Erişim metotları sağlamayarak, toString metodunu, ileride değişebileceğini belirtseniz bile sınıfınız için bir API haline getirmiş olursunuz.

  1. No comments yet.
  1. No trackbacks yet.

Please leave these two fields as-is:

Protected by Invisible Defender. Showed 403 to 653.715 bad guys.

%d blogcu bunu beğendi: