Ada, C#, D, Dart, Delphi, Java, Logtalk, Object Pascal, Objective-C, PHP, Racket, Seed7, Swift. Bu dillerin ortak bir yanı var. Hepsinde interface vari sınıf yapılarını bulmak mümkün. İnterface sınıfların ne olduklarını ve nasıl kullanıldıklarını bu yazımda açıklamaya çalıştım. İnterface sınıflar benim için iyi bir tasarımın vazgeçilmez ögeleri. Bu yazımda neden daha aktif kullanılmaları gerektiğinden bahsetmek istiyorum.
Bu yazı bir şahsın “2015 yılına geldik, hala Spring’le birlikte interface sınıf mı kullanmak zorundayız?” söylemine cevaben oluştu. İnterface sınıflardan faydalanmak için birçok neden var. Bunların en başından tartışmasız DIP (Dependency Inversion Principle – Bağımlılıkların Tersine Çevrilmesi Prensibi) gelir. Şimdi aşağıdaki Java kod örneğine bakalım:
[source language=”java”]
ArrayList list = new ArrayList();
[/source]
Bu şekilde kod yazan DIP prensibini anlamamış demektir, çünkü oluşturulan bağımlılığın yönü somut bir sınıfa doğru. list değişkeni somut bir sınıf olan java.util.ArrayList sınıfına işaret etmektedir. Denklemin her iki tarafında da somut bir sınıf yer almaktadır. DIP prensibini uygulamak için denklemin iki tarafını da soyutlaştırmamız gerekiyor. İlk adımı şöyle atabiliriz:
[source language=”java”]
List list = new ArrayList();
[/source]
Değişkenin veri tipini bir interface olan java.util.List olarak değiştirdim. Bir interface sınıfının kullanıldığı kodun ifade etmeye çalıstığı bir şey bulunmaktadır. Bunun ne olduğunu gerçek hayattan bir örnek vererek, açıklamak istiyorum. Markete alışverişe gidiyorsunuz. Bir zaman sonra ödeme yapmak için kasada beklemeye başlıyorsunuz. Sıra size geldiğinde, kasiyer sadece 10 TL banknotlar ile ödeme yapabilirsiniz diyor. Siz doğal olarak “neden ayrım yapıyorsunuz, her türlü para ile ödeme yapabilmeliyim” diyorsunuz. Haklısınız.
Burada para terimini soyut bir yapı olarak kullandınız. Sizin için 10, 50 ya da 100 TL’lik banknotların hepsi birer para. Para terimi sizin için birçok banknot türüne işaret eden soyut ve genel bir kavram. Cüzdanınızda bulunan paranın ne türde olduğu sizi ilgilendirmiyor. Önemli olan miktarı ve parayı ne için kullanabileceğiniz. Lakin karşınızda sadece 10 TL’lik banknotların geçerli olduğu bir market çıktığında, bunu anlamlı bulmuyorsunuz, çünkü sahip olduğunuz para kavramı her cinsten parayı kapsıyor. Market sadece 10 TL’lik banknotları kabul ederek, ödeme şeklini daraltmış durumda. Neden bir market her türden parayı kabul etmek varken, 10 TL’lik banknotları tercih ediyor olsun? Şimdi marketi bir programcı olarak düşünelim. Programcıların çogu kod yazarken 10 TL’lik banknot kabul eden market gibi davranır. Aşağıdaki koda bakalım:
[source language=”java”]
class Market{
10TLBanknot para = new 10TLBanknot();
}
[/source]
Market sınıfı sadece ve sadece 10TLBanknot veri tipinde olan bir yapı kullanabiliyor. Bu markete gidip sadece 10 TL’lik banknotlarla ödeme yapmaktan farksız bir durum. Para değişkeni somut bir yapıya işaret etmektedir. Somut bir şey elle tutulur, gözle görülür bir yapıdır. 10 TL’lik banknot somut bir yapıdır. 10 TL’lik banknot 10 TL’lik banknot anlamına gelmektedir. Buna karşın para terimi her türden banknot anlamına gelmektedir. 10 TL’lik banknot geçerli dendiğinde, başka hiçbir türden banknot geçmez denmiş olur. Para geçerli dendiğinde, her türlü banknot geçerli denmiş olur. Somut yapılar kullanıldığında hedef gösterilmiş olunur. Soyut yapılar kullanıldığında hedefin ne olduğunu tam olarak görmek mümkün değildir, çünkü cüzdandan para olarak 10 TL’lik banknot da çıkabilir, 100 TL’lik banknot da. Somut kavramlar kullanıldığında seçim yelpazesi daraltılır, soyut kavramlar kullanıldığında seçim yelpazesi genişletilir. Kod yazarken somut kavramlar kullanıldığında, kod doğrudan seçilmiş olan somut yapıya bağlanır ve değiştrilmesi zora girer. Buna karşın soyut yapılar kullanıldığında, kodu soyut yapının değişik türlerini kullanmak suretiyle değiştirmek kolaylaşır. Buna esnek bağ oluşturma ismi verilmektedir.
Şimdi tekrar aşağıda yer alan koda göz atalım. Bu kod ne ifade etmektedir?
[source language=”java”]
class Listeler{
List list = new ArrayList();
}
[/source]
Denklemin sol tarafı soyut olan List sınıfını kullanarak burada soyut listeler kullanılır demekle birlite, denklemin sağ tarafındanki ArrayList “sol taraf istediğini söylesin, burada somut bir liste sınıfı kullanılmaktadır” demektedir. Sağ tarafın ağırlığı daha fazladır ve onun dediği geçerlidir. Bu durumda kod bünyesinde somut bir yapı kullanmaya başlamış oluruz. Somut yapıların kullanılmalarındaki en büyük sıkıntı, zor değiştirilebilir olmalarıdır. ArrayList yerine LinkedList kullanmak isteseydik, yukarıda yer alan sınıfı degiştirip, yeniden derlememiz gerekirdi. Bunun yanı sıra yukarıda yer alan sınıfı nereye gönderirseniz gönderin, yanında ArrayList sınıfını da olmak zorundadır. Bu sebeplerden dolayı Listeler sınıfı DIP ile uyumlu değildir. Bu sınıfı DIP ile uyumlu hale getirebilmek için new ile yeni nesne oluşturma işlemini sınıfın dışına almamız gerekmektedir. Bu işleme bağımlılıkların enjeksiyonu (dependency injection) ismi verilmektedir ve aşağıda yer alan Market sınıfında uygulanmaktadır.
Şimdi aşağidaki sınıfa bir göz atalım:
[source language=”java”]
class Market{
private Para para;
Market(Para para){
this.para = para;
}
}
[/source]
Konstrüktör aracılığı ile herhangi bir Para implementasyonunu Market sınıfına enjekte edebilir hale geldik. Artık bu markette her türlü banknot kullanılabilmektedir. Market sınıfı her türlü Para implementasyonunu kabul eden soyut bir yapıya kavuştu ve böylece test edilebilirligi de artmış oldu.
Tekrar “2015 yılına geldik, hala Spring’le birlikte interface sınıf mı kullanmak zorundayız?” sorusuna geri dönmek istiyorum. Spring çatısında teknik olarak bu zorunluluk ortadan kalktı. Spring somut sınıflardan nesneler oluşturup, başka sınıflara enjekte edebiliyor. Küçük çaplı uygulamalarda belki interface sınıfların kullanımı o kadar da çok mühim olmayabilir. Lakin müşteri gereksinimlerinin sürekli değişiklige ugradığı dinamik bir ortamda interface sınıfların kullanımı uygulamanın geleceği açısından hayati olabilir. Burada uygulamanın yeni müşteri gereksinimleri doğrultusunda yeniden yapılandırılabilmesi ve yoğrulabilmesi önem taşımaktadır. Bunu sağlayabilmenin tek yöntemi uygulama testlerinin oluşturulmasıdır. İnterface sınıfların ağırlıklı kullanıldığı uygulamalar mock, stub implementasyonları ve bağımlılıkların enjekte edilmesi tekniğiyle kolay test edilebilir hale gelmektedirler.
İnterface sınıfları arkasına gizlenmiş kod birimlerini kara kutu gibi görebiliriz. Kendi içlerinde nasıl işledikleri önem taşımamaktadır. Önemli olan nasıl bir kullanım arayüzüne sahip olduklarıdır. Kendi mock ve stub implementasyonlarımız aracılığı ile ayrıca kara kutunun davranış biçimini değiştirerek, uygulamayı istediğimiz şekilde test edebiliriz.
EOF (End Of Fun)
Özcan Acar
Yorumlar
“2015 Yılına Geldik, Hala Spring’le Birlikte İnterface sınıf mı Kullanmak Zorundayız?” için 3 yanıt
Hocam yine çok açıklayıcı çok güzel bir makale olmuş. Market örneğine bayıldım. Teşekkürler.
Hocam süper bir yazı olmuş teşekkür ediyorum…
Kaleminize sağlık, keyifle okudum. paylaşım için teşekkür ederim.