Gönderen H.Levent KARAGÖL Etiketler:

Bu makalemizde, modüllere mülayimlik katarak birbirleri ile daha iyi geçinmelerini sağlayan Adapter Pattern'i inceleyeceğiz.

Not  : Design Patterns serisindeki tüm makalelerde, 10 yıl tecrübeli uzman junior developer olarak rol alacağız ve bizden çözmemizi talep ettikleri sorunlara, kendi yöntemlerimizle yaklaşacağız. Sıkıntı bastığı zamanda da, eski bir dostumuz olan Senior JavaScript Architect Hamdi'den destek isteyeceğiz. Makalenin sonlarına doğru da, Pattern'in formal tanımına, diğer kullanım alanlarına ve muhtemel sıkıntılarına değineceğiz.

Talep : Strategy Pattern makalemizin sonunda ortaya çıkarttığımız, uluslararası turizm acenteliği yapan bir firma için taşıt ve mesafeye göre maliyet hesabı yapan programı hatırlıyor musun? Hatırlamayanlar işin aşağıda tekrar veriyorum.

Aynı firma bizi tekrar görüşmeye çağırdı. Ellerinde, başka bir firmadan edindikleri ve uçakla ulaşım maliyetlerini hesaplayan bir sınıf olduğunu, bunu da var olan sisteme eklemek istediklerini söylediler. Bu sınıf hem yurt dışı, hem de yurt içi uçuşlara göre maliyet analizi yapıyormuş ancak firma uçak seçeneğini sadece yurt dışı uçuşlar için kullanacakmış. Bir de bu sınıf, vergi indirimine tabii olan kurumlar için alan vergisini ayrıca hesaplıyormuş ancak bunun da firma için bir anlamı yokmuş, tüm giderler ile birlikte toplam maliyet ile ilgileniyorlarmış. Bahsi geçen sınıfı da mail ile aşağıdaki gibi göndermişler.


Al sana Open Closed! Welcome to the real world.

Bir kere, bu sınıfı bize yazdırmamışlar, bir başkasından beleşe getirmişler, bu birinci ayıp. Sırf beleş olsun diye bizim güzelim mimarimizi bozabileceklerini akıllarına bile getirmemişler bu da ikinci ayıp. Hadi, ikincisi neyse de, birincisi çok ayıp olmuş!


Neyse, sonuç itibariyle akşam eve dönüşte elimiz boş gitmeyelim diye işi kabul ettik. Aslında JavaScript'te olmamızın imkanlarından faydalanarak, aşağıdaki gibi, ortak Interface'i uygulamadığı için başka dillerde yapamayacağımız bir çözümle olaya yaklaşabiliriz.

Ancak böyle bir şeyi yapmayı düşünmüyoruz çünkü bu bizim Open Closed prensibimizde bir delik açacaktır ve bu tarz işlerde açılan her delik, kendinden sonra açılacak olan daha büyük bir deliğin öncüsüdür. Tecrübe ile sabittir ki, bundan sonra artık bu kod iflah olmaz.

O halde bir bilene soralım dedik, telefonla Senior JavaScript Architect arkadaşımız Hamdi'yi arayıp durumu anlattık. Hamdi, telefonda Adapter Pattern'den bahsetti ve JavaScript'te bu Pattern'i nasıl kullanabileceğimize ilişkin aşağıdaki örneği mail ile bize gönderdi.

Buradaki örnekte, bizim kodumuz "expectedFunction" adında bir fonksiyonu çağırarak işi bitirmek istiyor. Ancak bu işi yapan "AnotherClass" isimli sınıfın, "anotherFunction1" ve "anotherFunction2" isimli iki fonksiyonu mevcut ve bizim istediklerimizi, ancak bu iki fonksiyonu birden çağırarak elde edebiliyoruz. Bu yüzden kendi kodumuzu değişirmek yerine, kendi kodumuzla hedef sınıf arasına girerek, arada adaptör görevi görecek bir sınıf yazıyor, gerekli çağrımları da bu sınıf içerisinde gerçekleştiriyoruz. Bu sınıfın bize bakan yüzü bizim beklediğimiz gibi, karşı taraftaki sınıfa bakan yüzü de karşı tarafın beklediği gibi oluyor.

Adapter Pattern'i tarif etmek için genellikle aşağıdaki şekil kullanılır.



Burada araya girecek Adapter sınıfının içeriği, tamamen ihtiyaca bağlıdır. Bire-bir fonksiyon çağrımı yapıp sadece parametreleri de değiştirebilir, pek çok fonksiyondan oluşan bir iş akışını da işletebilir, dağıtık ortamlara bağlanıp veriler toplayarak bize cevap da dönebilir. Burada önemli olan, her iki tarafında bulunan sınıfların da değişmesine ihtiyaç duymayacak şekilde araya girmesidir. Öyle ki, her iki taraf da karşı tarafın kendine uyduğunu sanarak mutlu olmalı.

Bu bilgiler ışığında, "Plane" sınıfı için bir "PlaneAdapter" sınıfı yazacak şekilde, "program.js" dosyamızı aşağıdaki gibi değiştirelim.


Bu örneğin hazır yazılmışını çalıştırmak için buraya tıklayabilirsin. Karşına aşağıdaki gibi bir sayfanın gelmesi gerekiyor.



Gördüğün gibi, ne eski kodumuzda Open Closed prensibinden ödün verdik, ne de bize verilen "Plane" sınıfını yeniden yazmak zorunda kaldık.

Hepimiz biliyoruz ki gerçek dünya, fizik derslerindeki gibi hava sürtünmesinin ihmal edilebildiği bir yer değildir. Biz ne kadar güzel sistemler tasarlasak da, sistemle alakası olmayan çirkin bazı parçaların da sisteme dahil edilmesi her zaman gerekecektir. İşte böyle durumlarda aceleci davranmak yerine, ilgili parçalar için uygun adaptörler yazarak, mimarimizi bozmadan güzel sonuçlara ulaşabileceğimizi unutmamamız lazım.


Formal Tanım

Adapter Pattern, bir sınıfın Interface'ini, Client'ın beklediği başka bir Interface'e çevirir. Bu Pattern, birbirleri ile Interface uyuşmazlığı sebebiyle çalışmaya uygun olmayan sınıfların birlikte çalışabilmesine imkan verir.


Başka Nerelerde Kullanılabilir Mesela?
  • Genellikle, bizim yazdığımız kodlar ile bizim yazmadığımız kodların birleşim noktalarında kullanılır.
  • Örneğin offline dosyaları parse ederek işlemekten sorumlu olan bir serviste, her dosya tipi için ayrı bir adapter yazılarak, değişik formattaki dosyaların tek bir formata indirgenmesinde ve çekirdekte bu tek format üzerinde çalışmakta kullanılabilir.
  • Farklı bir örnek olarak, n tane kuruma bağlanarak tahsilat yapan bir bankada, her bir kuruma bir adaptör yazarak, asıl çekirdekte sanki tek bir kurum varmış gibi huzur içerisinde kod yazmada kullanılabilir.


Hiç mi Günahı Yok?

Günahı yok, sevabı var. Yalnız Deadline'a yakın projelerde, panik sebebiyle adapter yazmayı bırakıp, ıvır zıvır ne varsa sisteme ekleyen bir programcı varsa, günah onun ve onu sıkıştıranındır.


Makalenin sonuna geldik. Öğrendiklerimizi özetleyelim.
  • Adapter Pattern genel olarak, bir sınıfın Interface'ini istenen hale döndürmek için kullanılır.
  • Bizim yazdığımız güzelim mimariye uyum sağlamayan çirkin bir kod bloğunu programımıza dahil etmek zorunda olduğumuz zaman, bunu Adapter Pattern ile çevreleyerek mevcut mimarimizin bozulmasının ve prensiplerden ödün vermenin önüne geçebiliriz.
  • Adapter sınıfı içerisinde yapılacaklar tamamen ihtiyaca göre değişiklik gösterir. Bire-bir fonksiyon çağrımı yapıp sadece parametreleri de değiştirebilir, pek çok fonksiyondan oluşan bir iş akışını da işletebilir, dağıtık ortamlara bağlanıp veriler toplayarak bize cevap da dönebilir. Burada önemli olan, her iki tarafında bulunan sınıfların da değişmesine ihtiyaç duymayacak şekilde araya girmesidir.
  • Sistem geneline uymayan bir kod bloğunu sisteme dahil etmek için aceleci olmamak ve sistem mimarisini koruyacak Adapter'ı yazmayı ihmal etmemek gerekir (Bu söz hem geliştirici, hem de proje yöneticilerine gitsin).