Adapter Pattern, mevcut sınıfların arayüzünü değiştirmeden uyumluluk sağlamayı amaçlayan bir tasarım desenidir. Bu desen, özellikle var olan kodlarla yeni sistemler arasında uyum sağlamak için kullanışlıdır. Adapter Design Pattern, iki farklı sınıfın birlikte çalışabilmesi için aralarında bir köprü görevi gören bir “adapter” sınıfı oluşturarak gerçekleştirilir.
Adapter Pattern’in temel amacı, bir sınıfın arayüzünü, başka bir sınıfın beklediği bir arayüze dönüştürmektir. Bu sayede, birlikte çalışamayan sınıflar arasında uyum sağlanır. Adapter Pattern, hem nesne tabanlı (object adapter) hem de sınıf tabanlı (class adapter) olarak uygulanabilmektedir. Java, çoklu kalıtımı desteklemediğinden genellikle nesne tabanlı adaptör kullanımı daha yaygındır.
İçerik
- 1 Adapter Pattern Kullanımının Avantajları ve Dezavantajları
- 2 Adapter Pattern Örnek Senaryo
- 3 Örneğin açıklanması
- 4 Adapter Pattern Kullanım Alanları
- 4.1 1. Eski ve Yeni Kodun Entegrasyonu
- 4.2 2. Farklı Arayüzlerin Uyumluluğu
- 4.3 3. Üçüncü Parti Kütüphaneler
- 4.4 4. Çoklu Arayüz Desteği
- 4.5 5. Sistem Bağımlılıklarının Azaltılması
- 4.6 6. Platform Bağımsızlık
- 4.7 7. Değişken Formatlar ve Protokoller
- 4.8 8. İstemci Uyumluluğu
- 4.9 Adapter Pattern Kullanım Örnekleri
- 5 Kod Örneği: Üçüncü Parti Kütüphane Entegrasyonu
- 6 Sonuç
Adapter Pattern Kullanımının Avantajları ve Dezavantajları
Avantajları
- Uyumlu Hale Getirme: Farklı arayüzlere sahip sınıfları bir araya getirerek uyumsuzluğu giderir. Bu sayede farklı yapıda geliştirilmiş sınıfları birlikte kullanabiliriz.
- Yeniden Kullanım: Adaptörler, mevcut sınıfları değiştirmeden yeni bir arayüzle kullanmamızı sağlar. Bu, kodun yeniden kullanılabilirliğini artırır.
- Esneklik: Adaptörler, sistemi esnek hale getirir. Yeni sınıfların eklenmesi veya mevcut sınıfların değiştirilmesi gerektiğinde kolayca adapte edilebilirler.
Dezavantajları
- Kod Karmaşıklığı: Adaptörler, ekstra bir katman eklediği için kod karmaşıklığını artırabilir. Bu da, bakımı zorlaştırır.
- Performans Kaybı: Adaptörler, arayüz dönüşümleri nedeniyle performans kaybına yol açabilir. Ancak bu genellikle önemsizdir ve optimize edebiliriz.
Adapter Pattern Örnek Senaryo
Bir medya oynatıcı uygulamamız olduğunu varsayalım. Bu uygulama, MediaPlayer
arayüzünü kullanarak ses dosyalarını çalabiliyor. Ancak, elimizde sadece MP3 dosyalarını çalabilen bir sınıf (Mp3Player
) var. Yeni bir gereksinim olarak, uygulamanın MP4 ve VLC formatındaki dosyaları da çalabilmesi gerekiyor. Bunu sağlamak için Adapter Pattern kullanarak mevcut sistemi genişletebiliriz.
MediaPlayer Arayüzü
public interface MediaPlayer {
void play(String audioType, String fileName);
}
Mp3Player Sınıfı
public class Mp3Player implements MediaPlayer {
@Override
public void play(String audioType, String fileName) {
if(audioType.equalsIgnoreCase("mp3")) {
System.out.println("Playing mp3 file. Name: " + fileName);
} else {
System.out.println("Invalid media. mp3 format supported only.");
}
}
}
AdvancedMediaPlayer Arayüzü
public interface AdvancedMediaPlayer {
void playVlc(String fileName);
void playMp4(String fileName);
}
VlcPlayer ve Mp4Player Sınıfları
public class VlcPlayer implements AdvancedMediaPlayer {
@Override
public void playVlc(String fileName) {
System.out.println("Playing vlc file. Name: " + fileName);
}
@Override
public void playMp4(String fileName) {
// Do nothing
}
}
public class Mp4Player implements AdvancedMediaPlayer {
@Override
public void playVlc(String fileName) {
// Do nothing
}
@Override
public void playMp4(String fileName) {
System.out.println("Playing mp4 file. Name: " + fileName);
}
}
MediaAdapter Sınıfı
public class MediaAdapter implements MediaPlayer {
AdvancedMediaPlayer advancedMusicPlayer;
public MediaAdapter(String audioType) {
if(audioType.equalsIgnoreCase("vlc")) {
advancedMusicPlayer = new VlcPlayer();
} else if(audioType.equalsIgnoreCase("mp4")) {
advancedMusicPlayer = new Mp4Player();
}
}
@Override
public void play(String audioType, String fileName) {
if(audioType.equalsIgnoreCase("vlc")) {
advancedMusicPlayer.playVlc(fileName);
} else if(audioType.equalsIgnoreCase("mp4")) {
advancedMusicPlayer.playMp4(fileName);
}
}
}
AudioPlayer Sınıfı
public class AudioPlayer implements MediaPlayer {
MediaAdapter mediaAdapter;
@Override
public void play(String audioType, String fileName) {
if(audioType.equalsIgnoreCase("mp3")) {
System.out.println("Playing mp3 file. Name: " + fileName);
} else if(audioType.equalsIgnoreCase("vlc") || audioType.equalsIgnoreCase("mp4")) {
mediaAdapter = new MediaAdapter(audioType);
mediaAdapter.play(audioType, fileName);
} else {
System.out.println("Invalid media. " + audioType + " format not supported");
}
}
}
Kullanım
public class AdapterPatternDemo {
public static void main(String[] args) {
AudioPlayer audioPlayer = new AudioPlayer();
audioPlayer.play("mp3", "beyond the horizon.mp3");
audioPlayer.play("mp4", "alone.mp4");
audioPlayer.play("vlc", "far far away.vlc");
audioPlayer.play("avi", "mind me.avi");
}
}
Örneğin açıklanması
Bu örnekte, AudioPlayer
sınıfı MediaPlayer
arayüzünü kullanarak farklı formatlardaki medya dosyalarını çalabiliyor. MediaAdapter
sınıfı, gerekli adaptasyonu sağlayarak VlcPlayer
ve Mp4Player
sınıflarının kullanılmasını mümkün kılıyor. Bu sayede, yeni medya formatları eklemek ve mevcut sistemi genişletmek çok daha kolay hale geliyor.
Adapter Pattern Kullanım Alanları
1. Eski ve Yeni Kodun Entegrasyonu
Adapter Pattern, mevcut eski kodun (legacy code) yeni sistemlerle entegre edilmesi gerektiğinde sıkça kullanılmaktadır. Eski kodun değiştirilmesi zor veya riskliyse, adapter kullanarak yeni sistemin eski kodla uyumlu hale getirilmesi sağlanır.
2. Farklı Arayüzlerin Uyumluluğu
Farklı arayüzleri kullanan bileşenlerin birlikte çalışması gerektiğinde Adapter Pattern devreye girer. Örneğin, bir API’nin sunduğu arayüzün uygulamanızın beklediği arayüze dönüştürülmesi gerektiğinde bu desen kullanışlıdır.
3. Üçüncü Parti Kütüphaneler
Üçüncü parti kütüphanelerin, mevcut uygulamanın arayüzüne uygun hale getirilmesi gerektiğinde Adapter kalıbı kullanılmalıdır. Bu, kütüphane sağlayıcılarının sunduğu arayüzlerin uygulamanızın ihtiyaçlarını karşılaması için uyarlanması anlamına gelir.
4. Çoklu Arayüz Desteği
Tek bir sınıfın birden fazla arayüzü desteklemesi gerektiğinde Adapter kalıbı kullanılmaktadır. Bu, özellikle farklı istemcilerin farklı arayüzleri beklediği durumlarda yararlıdır.
5. Sistem Bağımlılıklarının Azaltılması
Adapter tasarım deseni, bağımlılıkları azaltmak için kullanılır. Bir sınıfın doğrudan başka bir sınıfa bağımlı olmasını engelleyerek, sistemin daha esnek ve sürdürülebilir hale gelmesini sağlar.
6. Platform Bağımsızlık
Farklı platformlar arasında taşınabilirliği sağlar. Örneğin, farklı işletim sistemlerinde çalışan yazılımların uyumluluğunu artırmak için bu deseni kullanabiliriz.
7. Değişken Formatlar ve Protokoller
Farklı veri formatları veya iletişim protokollerinin kullanılması gerektiğinde Adapter tasarım kalıbı devreye girer. Örneğin, bir uygulamanın XML ve JSON formatlarını desteklemesi gerekiyorsa, bu desen kullanılabilir.
8. İstemci Uyumluluğu
Farklı istemcilerin aynı hizmeti farklı yollarla talep ettiği durumlarda Adapter Pattern kullanabiliriz. Bu, istemcilerin beklediği spesifik arayüzlerin sağlanması için adaptörlerin kullanılmasını içerir.
Adapter Pattern Kullanım Örnekleri
- Veri Dönüşümleri: Farklı veri kaynaklarından gelen verilerin tek bir formatta işlenmesi gerektiğinde.
- Grafik Kütüphaneleri: Farklı grafik kütüphanelerinin birlikte kullanılması gerektiğinde.
- Dosya Sistemleri: Farklı dosya sistemlerinin veya veri tabanlarının aynı uygulama içinde kullanılması gerektiğinde.
- Ağ Protokolleri: Farklı ağ protokollerinin desteklenmesi gerektiğinde, örneğin HTTP ve FTP.
Kod Örneği: Üçüncü Parti Kütüphane Entegrasyonu
Şimdi bir örnek de gerçek dünya da karşılaşabileceğimiz bir sorun ile ilgili paylaşmak istedim. Diyelim ki bir e-ticaret uygulamanız var ve yeni bir ödeme sağlayıcısı eklemeniz gerekiyor. Ancak, mevcut sisteminiz PaymentProcessor
arayüzünü kullanıyor ve yeni sağlayıcı bu arayüzü desteklemiyor. Adapter Pattern kullanarak bu sorunu çözebiliriz.
PaymentProcessor Arayüzü
public interface PaymentProcessor {
void processPayment(double amount);
}
Eski Ödeme Sınıfı
public class OldPaymentProcessor implements PaymentProcessor {
@Override
public void processPayment(double amount) {
System.out.println("Processing payment of $" + amount + " using OldPaymentProcessor.");
}
}
Yeni Ödeme Sağlayıcı Sınıfı
public class NewPaymentService {
public void makePayment(double amount) {
System.out.println("Processing payment of $" + amount + " using NewPaymentService.");
}
}
PaymentAdapter Sınıfı
public class PaymentAdapter implements PaymentProcessor {
private NewPaymentService newPaymentService;
public PaymentAdapter(NewPaymentService newPaymentService) {
this.newPaymentService = newPaymentService;
}
@Override
public void processPayment(double amount) {
newPaymentService.makePayment(amount);
}
}
Kullanım
public class AdapterPatternExample {
public static void main(String[] args) {
PaymentProcessor oldProcessor = new OldPaymentProcessor();
oldProcessor.processPayment(100.0);
NewPaymentService newService = new NewPaymentService();
PaymentProcessor adapter = new PaymentAdapter(newService);
adapter.processPayment(200.0);
}
}
Bu örnekte, PaymentAdapter
sınıfı, NewPaymentService
sınıfını PaymentProcessor
arayüzü ile uyumlu hale getirir. Bu sayede, mevcut sisteminizi değiştirmeden yeni ödeme sağlayıcısını kullanabilirsiniz. Adapter Pattern, bu tür senaryolarda esneklik ve uyumluluk sağlar.
Sonuç
Adapter tasarım deseni, bu esnekliği ve genişletilebilirliği sağlayarak yazılım geliştirme sürecinde önemli bir rol oynar.
Burada anlatılan tasarım deseninin kodlarına GitHub linki üzerinden ulaşabilirsiniz.