Adapter Design Pattern

adapter design pattern

Adapter Design Pattern

Merhaba sevgili dostlar!

Tasarım desenleri incelediğimiz makale serimize devam ediyoruz. Eğer önceki makaleleri de takip etmişseniz, Yaratımsal Tasarım Desenleri (Creational Design Patterns) kategorimizi tamamladığımızı biliyorsunuz demektir. Peki o halde, sırada hangi kategori var dersiniz?

Efendim sıradaki tasarım deseni kategorimiz Yapısal (Structural) Tasarım Desenleri ismini alıyor. Bu ada bakarak söz konusu kategorideki desenleri, nesnelerin birbiriyle olan fiziksel ilişkilerini düzenlemek amacıyla kullandığımızı söyleyebiliriz. Bu kısa tanımdan sonra, bu kategorinin ilk tasarım deseni olarak; Adapter Design Pattern’i incelemeye başlayalım dilerseniz.

Biliyorsunuz ki problemi anlamadan, herhangi bir tasarım deseninin nasıl çalıştığını anlamak zor oluyor. O zaman gelin, ilk olarak problemin üzerinde biraz duralım.

Problem

Bir uygulamayı geliştirirken, tüm ihtiyaçlarımızı baştan aşağıya biz karşılamıyoruz doğal olarak. Bir aygıttan veri okumak ya da çıktıyı başka bir uygulamaya göndermek gibi ihtiyaçlarımız için üçüncü parti bileşenlerden faydalanabiliyoruz. Bu bileşenler, bize bazen bir API (Application Programming Interface) aracılığı ile sunuluyor, bazen de bir COM nesnesi olarak bazen de Web Service aracılığı ile. Hangi yöntemle sunulursa sunulsun bizim tek yapacağımız, ihtiyaç duyduğumuz nesnenin ilgili metodunu kullanmaktan ibaret oluyor.

Bu metotların dönüş değerleri ya da aldığı parametreler, yani kısaca arayüzleri, bizim geliştirdiğimiz istemcinin beklediği arayüzden farklı ise ne yapacağız?

Bu soruya ilk olarak şöyle yanıt verebilirsiniz; üçüncü parti bileşenin arayüzü neyse, biz de o arayüze göre bir istemci yazarız. Ancak bu her zaman mümkün olmayabilir. Örneğin, aynı işleve sahip fakat arayüzleri farklı, birden fazla nesne varsa ne yapacaksınız?

Gelin buna daha somut bir örnek verelim; uygulamanız, bir optik okuyucu cihazından gelen verileri alsın. Bu cihazın API’ sini kullanarak söz konusu veri okuma işlemini yapıyorsunuz. Eğer istemcinizi bu API’ ye göre yazarsanız, o zaman uygulamanız sabit bir cihaza göre yazılmış olacaktır. Bunun pek de istenen bir şey olmadığını düşünüyorum.

Demek ki, üçüncü parti bileşenin arayüzünü, istemcinin beklediği arayüze dönüştürmemiz çok daha mantıklı olacaktır.

Kavramlar soyut olduğunda, üzerinde düşünülmesi de zor olabiliyor haliyle. O zaman bu problemi en basit haliyle günlük hayatımıza uyarlayalım bakalım.

Bir tatil için, İngiltere’ye gittiniz. Otelinize girdiniz ve telefonunuzun şarjının bitmek üzere olduğunu fark ettiniz hemen çantanızdan şarj aletini çıkarttınız. Tam prize takacaktınız ki! O da ne! Priz beklediğiniz gibi değil.


Bu durumda, ne yapabilirsiniz? Duvardaki prizi değiştiremezsiniz. E şarj aletini değiştirmek de mantıklı değil (Diğer elektrikli cihazların mesela tıraş makinesinin fişi ne olacak?) . O halde, prizin arayüzünü sizin beklediğiniz arayüze çeviren bir adaptöre ihtiyacınız var.

İşte bu! Uygulama geliştirirken yaşayabileceğiniz problemi, günlük hayatta da yaşıyoruz ve çözüm olarak adaptör nesneler kullanıyoruz. O halde aynı çözümü kendi mimarimiz için neden uygulamayalım?

Çözüm

Öncelikle, geliştirdiğimiz istemcinin beklediği arayüzü tasarlamam gerekiyor (fişinizi nereye takabiliyorsunuz ?) . Sonra da adapte edilecek nesneye erişen bir sınıfa ihtiyacım olacak (duvardaki priz ne istiyor?). İşte bu kadar! Hadi o zaman bunları koda bir senaryo dâhilinde uygulayalım. Farz edelim ki, POS cihazından gelen veriyi okumak üzere bir uygulama yazıyorsunuz. Cihazın API’si tarafından nesnemiz de aşağıdaki gibi olsun:

Daha önce de belirttiğim gibi, bu örnekte de düşünmem gereken şeylerden biri şu olmalı: uygulama sadece bir POS cihazına (ve onun API’sine) bağlı olmamalı. Madem öyle, tüm POS cihazları ile çalışabilecek bir adaptör arayüzü tasarlamamız gerekiyor. İşte o arayüz geliyor:

İstemci, işte bu arayüz üzerinden cihaz API’si ile iletişim kurabilecek. POS cihazı ile çalışacak olan her sınıfı bu interface’den implemente etmem gerekiyor. Şu anda elimde bulunan X Bankın POS cihazı için gereken sınıfımı aşağıdaki gibi yazıyorum:

Bu sınıfa lütfen dikkat edin. Tanımladığımız interface’den gelen ReadCardData() metodu, adapte edilecek nesnenin (XBankPOSReader) ilgili metodunu (ReadFromCard()) çağırıyor yalnızca.

Yani istemci, XBankCardReaderAdapter adaptör nesnesine erişecek (şarj aletini adaptöre takacak). Adaptör de, asıl nesne üzerindeki ihtiyacımız olan metodu çağıracak (adaptörü duvardaki prize takacak). Hadi o zaman, istemcimizin kodlarını görelim:

İşte bu kadar! Kodun geliştirilebilirliği ortada duruyor. Bu uygulamanın başka bir kart okuyucu API’siyle çalışması gerektiğinde, geliştiricinin yapması gereken tek bir şey var. Yeni bir sınıfı Interface’den üretmek ve yukarıdaki 13. Satırda, bu sınıfın örneğini almak. Bu kadar…

Ha unutmadan, adapter tasarım deseninin UML diyagramı da burada dursun:

Evet, sevgili tasarım deseni severler… Bir yazımızın daha sonuna gelmiş bulunuyoruz. Bir sonrakine dek kendinize iyi bakın…

Kodla kalın…

5 thoughts on “Adapter Design Pattern

  1. Şu an kodlar tam kafamda oturmadı, sitenizi yer imlerine aldım daha sonra sakin kafa ile tekrar bakacağım, teşekkürler ufuk açıcı bir yazı olmuş.

  2. Öncelikle çok teşekkürler mükemmel yazılarınız ve ufkunuz için. Yalnız anlamadığım birşey var hocam, bu örnek özelinde sormak gerekirse adapter yerine new XBankPosReader, new YBankPosReader şeklinde sınıfları ayrı ayrı yaratıp öyle kullansak main metodunda ne kaybetmiş oluyoruz ? Tekrar teşekkürler

    1. Selam. Buradaki örnek açısından, main metodunda dediğiniz gibi yazdığınızda hiçbir şey kaybetmezsiniz. Ancak benzer bir işlemi, daha kompleks bir yapıdaki bir modül olarak düşünün… Mesela, tercihin kullanıcıya bağlı olduğu durumda ya da genişleyebilir bir mimaride istemci koduna müdahale etmemeli onu sadece geliştirebilmelisiniz.

      Benim yapmış olduğum bir projeden bir örnek verirsem belki daha akılda kalıcı olur. Bir yakıt dolumistasyonunda tankların içindeki yakıtı ısı ve basınç değerlerini gösteren bir uygulama geliştiriyorum. Tanklardaki basınç, sensörler aracılığı ile okunuyor. Fakat her iki sensörün veri okuma portu ve gönderdiği data biraz farklı. Hangisine göre yazmalıyım? Peki ya sensör bir şekilde bozulur ve yerine alınan yenisi, bambaşka bir yoldan veri gönderirse ne olacak?

      O zaman benim basınç verisini bir adaptörden okumam şart. Veriyi X sensörü de gönderse y sensörü de gönderse, aynı formata dönüştürmem gerekiyor. İşte adaptörün işlevi bu.

Leave a Reply