Java Kodunuzun Nesne-Merkezli Olmadığının 10 İşareti – V: Kodunuzda hiç ya da çok az sayıda arayüz olması
Geliştirdiğimiz Java kodunun nesne-merkezli olmadığının 10 işaretinin neler olduğunu bu yazıda listelemiş ve ilk üç maddeyi ele almıştık. Şimdi de dördüncü maddeyi ele alalım:
Kodunuzda hiç ya da çok az sayıda arayüz olması. (No or very few interfaces in the code.)
Bu maddede ne demek istediğimi hiç lafı dolandırmadan söyleyeyim: Ben Java ile geliştirilmiş bir projeye bakıp da o projede arayüz (interface) göremezsem, ya da olsa bile projenin örneğin DAO nesneleri gibi iş mantığını içermeyen kısımlarındaysa, o projede herhangi bir tasarım faaliyetinin yapılmadığına karar veririm. Çünkü arayüz kullanmadan karmaşılığı ve değişimi yönetebilmek mümkün değildir.
Kamaşık yapıları anlaşılır hale getirmenin en temel yöntemi, bölüp parçalamak ya da daha teknik ifadeyle soyutlamaktır (abstraction). Bölüp parçalayarak yeni soyutlamalar yapmak, zihnimizin en temel anlama ve problem çözme mekanizmasıdır. Yazılım geliştiriken bölüp parçalamanın sebebi ise temelde sorumlulukları ayırmaktır ki bunu da kadim “separation of concerns” yani “ilgi alanlarının ayrılması” prensibi ile açıklıyoruz.
Nesne merkezli teoride öncelikle üzerine eğilinecek, soyutlanacak, ayrılacak ilgi alanı, sorumluluklardır. Nesne-merkezli diller de önceliği daima sorumluluklara verir. Nasıl veri merkezli yaklaşım, verilerin sağlıklı bir şekilde modellenmesi, saklanması, sorgulanması gibi konuları önceliyor ve veriyi bu amaçlarla soyutluyorsa nesne-merkezli yaklaşım da önceligi sorumluluklara verip, her şeyi oradan başlatıyor. Burada ilgi alanlarını yani nesnelerin sorumluluklarını birbirinden ayırmak ve ilgili olanları bir araya toplamaktan bahsediyorsak, tabi olarak arayüzlerden yani interfacelerden bahsediyoruz demektir. Unutmayın, kodunuzda arayüz kullanmıyorsanız, muhtemelen veri merkezli ya da data-driven bir düşünce tarzına sahipsiniz demektir. Sonuçta yaptığınız da sorumluluklardan değil de veriden yola çıkarak yazılımınızı tasarlamanız ve geliştirmeniz demektir.
Öte taraftan değişimin en sıkıntılı tarafı, değişimin etkisinin yayılmasını önleyememektir. Değişimi maliyetli yapan çoğu defa kendisi değil, etkisini düzeltmenin maliyetinin yüksek oluşudur. Değişim ise çoğu kez, aynı sorumluluğu değişik şekillerde yerine getirme ihtiyacından doğar öyle ki sorumluluğun farklı şekillerde nerede ve nasıl yerine getirildiğini diğer nesneler bilmemelidirler ki değişimi kontrol altında tutabilelim (minimum info principle). Bir başka deyişle nesneler arasındaki bağımlılığı (coupling) arayüz seviyesinde tutmalıyız. Bu da tipik bir arayüz kullanımıdır.
Önce karmaşıklığı, arayüz kullanımıyla nasıl daha rahat yönetiriz onu görelim. Düşünün ki bir ERP yazılımı geliştiriyoruz ve ürettip sattığımız yiyecek olan ve olmayan ürünlerimiz var. Bu ürünleri fiyatları var ve benzer şekilde bu ürünler satılıp da müşterimize gönderilinceye kadar depolarımızda tutuluyorlar. Bu yapıyı şöyle modelleyebiliriz:
Görüldüğü gibi Cost ve Location, ürünün müşteriye olan maliyetini ve hangi adreste depolandığını gösteren sınıflardır. Food ve NonFood ise Product tipinden olup, satıldıkları için maliyetleri, depolandıkları için de depo adresleri olan alt sınıflardır. Peki bu ERP yapımızı bu şekilde kurguladık ve sattık. Daha sonra müşterilerimizin bazılarının danışmanlık ürünleri de olmaya başladı. Ne yapabiliriz? Muhtemelen Consultancy isimli bu yeni Product tipimizi ya NonFood‘un ya da doğrudan Product‘ın alt sınıfı yaparız. İlk hali denersek sınıf diyagramımız şöyle olur:
Consultancy tipi her halukarda bir Product‘tır dolayısıyla hem maliyeti hem de deposu vardır. Bu ise problemli bir durumdur. Çünkü en başta maliyeti olup da depolanmayan bir ürün düşünülmediğinden yani maliyet de depolanma da doğrudan Product olmaya bağlandığından Consultancy‘nin ifadesinde problem vardır. Çünkü Consultancy satılabilirdir ama depolanabilir değildir.
Bir başka durumu daha düşünelim: Bazı müşterilerimiz, eski kullandıkları bilgisayarları örneğin kurayla çalışanlarına dağıtıyor. Bu dağıtmayı da senede bir defa yapıyor. Ama müşteri bu sırada açığa çıkanları depoluyor ve sistemde bunların envanterini de tutmak istiyor. Yani elimizde satılmayan ama depolanan ürünler var ve bunun için Gift isimli yeni bir tipe ihtiyacımız var. Her iki sınıfın da problemi aslında sahip olmadıkları davranışı gösteriyor olmaları.
Genişçe düşünülmeden geliştirilen sistemlerde miras kullanımı genelde böyle problemlere yol açar. Yapılacak şey bu iki sınıfın yerine getirmediği davranışlar için sıradışı durum fırlatmasıdır. Hoş bir durum değil ama yapacak bir şey yok, çünkü değiştirmek çok pahalı. Peki nedir bu iki sınıfın durumunu sıkıntılı yapan şey? Aslında sıkıntı, tam bir tip problemidir. Yani Consultancy ve Gift, ürün olarak ifade edilmiş ama ilki satıldığı halde depolanmıyor, ikincisi depolandığı halde satılmıyor. Belli ki biz en başta Product‘ı tarif ederken hata yaptık. Product’ı, hem satılabilen hem de depolanabilen olarak tanımladık. Halbuki, depolanmadığı halde satılabilen, satılmadığı halde depolanabilen ürünlerin olabileceğini, dolayısıyla, ürün olmaklık ile satılabilmek ve depolanabilmenin ayrılması gerektiğini zamanında düşünmek gerekirdi. Bir şeyi o şey yapan ile o şeye eklemlenen farklı özellikleri ayırtedebilmek ancak arayüzler sayesinde olabilir. Bu anlamda bir tipin sınıfında onun asli özellikleri tanımlanmalı, arayüzünde ise devraldığı yetkinlikleri tanımlanmalıdır. Çünkü, yetkinlikler, bir şeyin en temel, ona has ve ayırt edici olan özelliklerinden değilse, muhtemelen sınıfta tanımlanmamalıdır, paylaşiıabilecek şekilde bir arayüzde tanımlanmalıdır. Bu anlamda sınıfın içine koyduklarınıza dikkat edin. Çünkü hiç bir interface yaratmadan sadece sınıftan devralarak ilerlerseniz, böyle durumlarla karşilaşmanız işten bile değildir. Aynı yapıyı arayüz tabanlı olarak şöyle tasarlamış olsaydık:
Yukarıdaki tasarımda satılabilir olmak ile depolanabilir olmak sırasıyla Sellable ve Storable arayüzleriyle ifade edilmiştir. Bu durumda Consultancy hem bir Product hem de Sellable tipi olurken, Gift de benzer şekilde hem bir Product hem de Storable‘dır.
Arayüz kullanmak her şeyden önce bir tasarım faaliyetidir; tasarım arayüzlerle başlar. Arayüzler, nihayetinde nesneleri oluşturulamadığından, tabiri caizse kodun değil de tasarımın parçasıdırlar. Arayüzleri kullandıkça daha kaliteli yazılımlar geliştireceksiniz.
Bol arayüzlü günler dilerim.
Toplam görüntülenme sayısı: 1304
Selman
03 Temmuz 2014 @ 12:07
Son derece güzel bir Türkçe ile güzel bir anlatım olmuş. Teşekkürler
Akin
03 Temmuz 2014 @ 19:53
Tesekkur ederim Selman beycigim 🙂
Soydan Ozcelik
07 Temmuz 2014 @ 11:06
Örnek çok iyi seçilmiş. Çok açıklayıcı.Lafı dolandırmadan gerçeği söylerek başlamakta ciddi bir merak uyandıyor.
Akin
07 Temmuz 2014 @ 13:12
Eyavllah Soydan, sagol 🙂