Java Kodunuzun Nesne-Merkezli Olmadığının 10 İşareti – VI: Aralarında benzerlik ilişkisi olan nesneleri modellemek için kalıtım kullanmamak
Geliştirdiğimiz Java kodunun nesne-merkezli olmadığının 10 işaretinin neler olduğunu bu yazıda listelemiş ve ilk dört maddeyi ele almıştık. Şimdi de beşinci maddeyi ele alalım:
Aralarında benzerlik ilişkisi olan nesneleri modellemek için kalıtım kullanmamak. (No inheritance hierarchy to model is-a relationships between the objects.)
Sebebi basit: Eğer kalıtım hiyerarşileriniz yoksa aynı tipten olan nesneleri ifade ederken pek de nesne-merkezli bir yöntem kullanmıyorsunuz demektir. Kalıtım kullanmazsanız polymorphismden faydalanamazsınız. Dolayısıyla kalıtım ve bunun sonucu olan polymorphism kullanılmıyorsa “neden nesne-merkezli bir dil kullanıyorsunuz?” sorusuna muhatap olursunuz? Hakikatten kalıtım ve polymorphism yok ise nesne-merkezli dilin en temel özelliklerinden sadece sarmalama ya da encapsulationu kullanıp sınıflar oluşturuyorsunuz demektir. Bu da Java’yı tam gücüyle kullanmamak, Java’yı C gibi kullanmak demektir.
Peki, kalıtım kullanılmadığında aralarında benzerlik ilişkisi olan yapıları nasıl ifade ederiz? Aşağıdaki basit sınıf bu durumu gösteriyor:
public class User{ private String username; private String password; private String name; private String lastname; private int type; public void register(){ if(type == 1){ doThis(); } else if(type == 2){ doThat(); } … } }
Yukarıdaki örnekte User sınıfının farklı alt tipleri kalıtım yoluyla yeni sınıflar oluşturarak değil de aynı sınıf üzerinde oluşturulmuş int tipinde type isimli bir değişkenle temsil ediliyor. Bunun en temel problemi, farklı tiplerin davranışlarını farklılaştırmak için metotlarda bol miktarda “if/switch” cümleleri kullanmak zorunda kalmaktır. Eğer kalıtım kullanılsaydı “if/switch” cümlelerini kullanmaya gerek kalmayacaktı çünkü register() metodunun farklı, override eden implementationları olacaktı.
Yukarıdaki durumda User sınıfının ve register() metodunun abstract olduğunu, dolayısıyla da onu extend eden sınıfların register() metodunu override ettiğini düşünebiliriz.
public abstract class User{ protected String username; … public abstract void register(); } -------------------------------------------------------------- public class IndividualUser extends User{ … public void register(){ doThis(); } } -------------------------------------------------------------- public class CorporateUser extends User{ … public void register(){ doThat(); } }
Yukarıdaki koddan da görüldüğü gibi bir kalıtım hiyerarşisi ve override edilmiş metotlar ile hem nesne açısından daha hoş bir yapıya ulaştık hem de “if/switch” cümlelerinden kurtulduk. Bu sayede tipe göre davranışı ayırt etmek artık JVM’in görevi haline geldi.
Öte taraftan kalıtım kullanmanın sarmalamaya (encapsulation) ters bir yapı olduğunu ve hiyerarşideki nesneler arasında ciddi bir bağımlılık (coupling) oluşturduğunu da söylemeliyiz. Örneğin User sınıfında yapılacak bir değişiklik alt sınıfları etkileyecek ve onların değişmesini gerektirebilecektir. Bu durum “inheritance violates encapsulation” yani “kalıtım sarmalamayı bozar” olarak ifade edilir. Bu sıkıntıdan dolayı kalıtım kullanırken dikkatli olmakta fayda vardır. Bu konu aslında burada hızlıca geçiştirilecek kadar basit değildir. Bir başka yazıda bunu ele alabiliriz.
Bol kalıtımlı günler dilerim.
Toplam görüntülenme sayısı: 1153