Önce-Arayüz Yaklaşımı (Interface-first Approach)

Daha önce “Java Kodunuzun Nesne-Merkezli Olmadığının 10 İşareti” serisinin bu yazısında da bahsetmiştim, ufak olsun büyük olsun, bir program yazacak ya da daha genişi, bir yazılım geliştirecekseniz, işe kodu yazarak değil, tasarlayarak başlamanız gereklidir. Tabi işin analizinin ve detaylı ihtiyaçlarının ortaya konduğunu varsayıyorum.

Önce-arayüz yaklaşımından kastım ise, aslında kodlamaya örneğin Java’da “class” anahtar kelimesi ile sınıf yerine “interface” anahtar kelimesiyle arayüz tanımlayarak başlamak değildir. Bununla kastettiğim, doğrudan koda girişmek yerine biraz geri durup, bilgisayara ya da mesela IDE’ye dokunmadan ihtiyaç üzerine düşünmek ve ona bir tasarım geliştirmektir. Nedir ihtiyaç, ana parçaları nelerdir, bu ihtiyaca nasıl bir çözüm getirilmelidir, çözümdeki temel soyutlamalar neler olmalıdır, aralarındaki ilişkiler nelerdir, kim ne yapmalı, kim neyi bilmeli vb. sorulara cevap vermek için kod yazmaya ihtiyacımız yoktur. Beyaz bir kağıda ya da bir kaç kişiyle çalışıyorsak tahtaya çiziktirerek de bunları yapabiliriz. Yaptığımız bu çiziktirmelerin daha standart ve okunabilir olması için UML’i kullanmayı seçebiliriz ama her iş için UML aracını açıp onu kullanarak bu işleri yapmak eğer işi daha bürokratik hale getiriyorsa bence ona da gerek yok. Dediğim gibi beyaz bir kağıt ya da tahta yeterli.

Problem şu, işe sınıftan başladığınızda yüksek bir ihtimalle IDE’nın başında kod yazıyorsunuzdur. Bu da sizi çok hızlı bir şekilde kodun detaylarına çekecektir. Kodun detayları sonuç olarak “gerçekleştirme” yani “implementation”dır. Gerçekleştirme ise ancak bir tasarım üzerine kurgulanırsa sağlıklı olabilir. “Nasıl arayüzle başlarken kağıt ya da tahta kullanıyorsam, sınıf ile başlayınca da aynı şekilde davranırım, sınıflarla tasarım yaparım” demeyin. İkisi farklı kavramlar ve farklı faaliyetlerin parçası. Arayüz daha çok tasarım faaliyetinin bir parçası, daha az oranda programlamanın parçası. Arayüzler üzerinden düşünmek demek yerine getirilecek olan ihtiyaçla ilgili sorumlulukları belirlemek ve onları uygun soyutlamalarla ifade etmek demektir. Düşünün bir projede arayüzleri kodlamaya ne kadar vakit ayırırsınız, sınıfı kodlamaya ne kadar vakit ayırırsınız. Ezici oranda vaktimizi sınıfları kodlamaya ayırırız, çünkü arayüzlere ayırdığımız vaktin çoğunluğunu arayüzleri kodlamaya değil, onları düzgün bir şekilde ortaya koymaya harcarız. Dolayısıyla probleminizin tasarımına, IDE başında sınıfları kodlayarak girişmek aslında tasarım ve kodlama faaliyetini içi içe geçirmek ve sonrasında da kod detayının ağır basmasından dolayı tamamen koda kayıp tasarımı ıskalamak demektir. Arayüzleri kullanarak tasarıma girişmemiz, yani önce arayüzler üzerine çalışıp sonrasında sınıflara geçmemiz, sağlıklı bir programlama için çok önemlidir.

Tasarım sırasında tabi olarak sınıfları da kullanırız. Çünkü her şeyin arayüzden oluştuğu bir sistemden bahsedemeyiz. Arayüzleri yerine getirecek sınıfların da olması gereklidir ki birileri arayüzde söz verilen sorumlulukları yerine getirsin. Bu yüzden arayüze İngilizce’de “contract” yani “anlaşma” ya da “hand shake” yani “el sıkışma” da denir. Yani sınıflar, arayüzleri yerine getirerek, o arayüzde söz verilen hizmetleri gerçekleştirirler. Ve bu sınıflardan hizmet alacak diğer sınıflar sadece arayüze bağımlı olurlar, arayüzü gerçekleştiren hangi sınıftır bunu bilmez. Hizmet alan sınıfın, aslında sadece arayüzü bilip, gerçekte hangi sınıfın hizmeti sağladığını bilmemesi polymorphism olarak adlandırılır. Programlamamızı tamamen arayüzler üzerinden kurgulamak ise, GoF’un kitabında “program to an interface not an implementation” olarak ifade edilen anlayıştır. Yani daima arayüze bağlı olarak program yazın, arayüzlerin gerçekleştirmeleri olan sınıflara bağımlılık oluşturmayın. Bu prensip Dependency Inversion prensibinin parçası olarak da şöyle “Depend upon abstractions. Do not depend upon concretions.” ya da Robert Martin’in makalesindeki gibi “ABSTRACTIONS SHOULD NOT DEPEND UPON DETAILS. DETAILS SHOULD DEPEND UPON ABSTRACTIONS.” ifade edilir

Tasarım sırasında sınıfların da gündeme gelmesi ile IDE başına oturup ta sınıfları yazmaya başlamak farklı şeylerdir.  Nihayetinde arayüzlerden işe başladığımız için odak noktamız sorumluluklar olacaktır. Bu da arayüzlerde metotların belirlenmesi ve nihayetinde hangi sınıfın hangi arayüzü gerçekleştireceği gibi çalışmaları içerecektir. Beyaz kağıtta bu ana yapıyı kurduktan sonra IDE’ye dönüp, kod detaylarına girişebiliriz. Ama şunu unutmayın: Arayüzleri belirlemek demek sorumluluk öbeklerini yani sorumlulukların arayüzlere nasıl dağıldığını belirlemek, sorumlulukların anlaşma detaylarını (ya da contractual details), yani metotların arayüz bilgilerini (isim, parametreleri ve return tipi), erişim durumlarını, fırlatacağı sıra dışı nesneleri belirlemek demektir. Arayüzlere öncelik vererek tasarıma girişmek, tasarımın konusu olmayan, ancak gerçekleştirmenin konusu olan detayları ele almayı ileriye atmak demektir.

Aşağıdaki örnekte aslen bir sınıf olarak düşünülen Modem’in, arayüz bağlamında düşünüldüğünde, sorumluluklara odaklanarak nasıl iki ayrı arayüz haline geldiğini görebiliriz.

Screen Shot 2014-12-25 at 18.25.39

Yukarıdaki örnekte doğrudan Modem isimli ve dört metoda sahip bir arayüz oluşturmak yerine neden iki farklı arayüz oluşturduğumuzu sorgulayabilirsiniz. İşte bu durum aslında tamamen arayüz odaklı düşünmenin bir sonucudur. Arayüzü önceleyen düşünme sistemi, arayüzleri olabildiğince atomik sorumluluk öbekleri olarak ele alır. Bu prensip de “interface segregation” yani “arayüzlerin ayrılması” olarak ifade edilir. Bu şekilde sadece data alıp-verecek sınıflar Data Channel, sadece bağlantı kuracak sınıflar da Connection arayüzüne bağımlı olurlar.

Bu konuya verilebilecek bir örnek, yukarıda bahsedilen yazıda verildi. Bu örnekte, işin başında arayüz kullanılmadığında sonrasında içine düşülecek sıkıntılı durumlar verilmiştir. Sorumlulukları en başından arayüzlerle ifade etmenin, sonrasında yapıyı ne kadar rahat ettirdiği bu örnekte görülebilir.

Bol arayüzlü günler dilerim.

Toplam görüntülenme sayısı: 1331