Adapter Design Pattern
Uyğun olmayan interface-lərə malik class-ların birlikdə işləməsinə imkan verir. Bu pattern, mövcud class-ın interface-ini başqa bir interface-ə çevirir və beləliklə, müxtəlif sistemlərin inteqrasiyasını asanlaşdırır.
Adapter pattern, real həyatda elektrik adapterlərinin işləmə prinsipinə bənzəyir. Məsələn, ABŞ-da istifadə olunan elektrik cihazını Avropa rozetkasına qoşmaq üçün adapter istifadə olunur. Proqramlaşdırmada da, Adapter pattern eyni məqsədlə istifadə olunur - uyğun olmayan interface-ləri bir-birinə uyğunlaşdırmaq.
Adapter Pattern-nin Əsas Xüsusiyyətləri
- Interface Conversion: Bir class-ın interface-ini başqa bir interface-ə çevirir
- Legacy Code Integration: Köhnə kod-un yeni sistemlərlə inteqrasiyasını təmin edir
- Reusability: Mövcud class-ların yenidən istifadəsini təmin edir
- Loose Coupling: Client və adaptee arasında zəif əlaqə (loose coupling) yaradır
Adapter Pattern-nin Növləri
Adapter pattern-nin iki əsas növü var:
- Class Adapter: Inheritance (varislik) istifadə edərək implementasiya olunur
- Object Adapter: Composition (tərkib) istifadə edərək implementasiya olunur
Adapter Pattern-nin Strukturu
- Target: Client-in istifadə etdiyi interface
- Adapter: Target interface-ni implement edən və Adaptee-ni istifadə edən class
- Adaptee: Uyğunlaşdırılması lazım olan mövcud class
- Client: Target interface-i istifadə edən class
Java-da Adapter Pattern İmplementasiyası
Object Adapter Nümunəsi
Koda bax
// Target interface
interface MediaPlayer {
void play(String audioType, String fileName);
}
// Adaptee interface
interface AdvancedMediaPlayer {
void playVlc(String fileName);
void playMp4(String fileName);
}
// Concrete Adaptee classes
class VlcPlayer implements AdvancedMediaPlayer {
@Override
public void playVlc(String fileName) {
System.out.println("Playing vlc file: " + fileName);
}
@Override
public void playMp4(String fileName) {
// Do nothing
}
}
class Mp4Player implements AdvancedMediaPlayer {
@Override
public void playVlc(String fileName) {
// Do nothing
}
@Override
public void playMp4(String fileName) {
System.out.println("Playing mp4 file: " + fileName);
}
}
// Adapter class
class MediaAdapter implements MediaPlayer {
private 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);
}
}
}
// Client class
class AudioPlayer implements MediaPlayer {
private MediaAdapter mediaAdapter;
@Override
public void play(String audioType, String fileName) {
// Built-in support for mp3 files
if (audioType.equalsIgnoreCase("mp3")) {
System.out.println("Playing mp3 file: " + fileName);
}
// MediaAdapter provides support for other formats
else if (audioType.equalsIgnoreCase("vlc") || audioType.equalsIgnoreCase("mp4")) {
mediaAdapter = new MediaAdapter(audioType);
mediaAdapter.play(audioType, fileName);
} else {
System.out.println("Invalid media type: " + audioType + " format not supported");
}
}
}
// Client code
public class AdapterPatternDemo {
public static void main(String[] args) {
AudioPlayer audioPlayer = new AudioPlayer();
audioPlayer.play("mp3", "song.mp3");
audioPlayer.play("vlc", "movie.vlc");
audioPlayer.play("mp4", "video.mp4");
audioPlayer.play("avi", "video.avi");
}
}
Class Adapter Nümunəsi
Java-da multiple inheritance dəstəklənmədiyinə görə, class adapter implementasiyası interface-lər vasitəsilə həyata keçirilir:
Koda bax
// Target interface
interface Target {
void request();
}
// Adaptee class
class Adaptee {
public void specificRequest() {
System.out.println("Specific request from Adaptee");
}
}
// Class Adapter
class ClassAdapter extends Adaptee implements Target {
@Override
public void request() {
specificRequest();
}
}
// Client code
public class ClassAdapterDemo {
public static void main(String[] args) {
Target target = new ClassAdapter();
target.request();
}
}
Two-way Adapter Nümunəsi
İki tərəfli adapter, hər iki interface-i implement edir və hər iki istiqamətdə çevirmə imkanı təqdim edir:
Koda bax
// First interface
interface Rectangle {
void drawRectangle();
double getArea();
}
// Second interface
interface LegacyRectangle {
void draw();
double calculateArea();
}
// First concrete class
class ModernRectangle implements Rectangle {
private double width;
private double height;
public ModernRectangle(double width, double height) {
this.width = width;
this.height = height;
}
@Override
public void drawRectangle() {
System.out.println("Drawing a modern rectangle with width " + width + " and height " + height);
}
@Override
public double getArea() {
return width * height;
}
}
// Second concrete class
class OldRectangle implements LegacyRectangle {
private double width;
private double height;
public OldRectangle(double width, double height) {
this.width = width;
this.height = height;
}
@Override
public void draw() {
System.out.println("Drawing an old rectangle with width " + width + " and height " + height);
}
@Override
public double calculateArea() {
return width * height;
}
}
// Two-way adapter
class RectangleAdapter implements Rectangle, LegacyRectangle {
private Rectangle modernRectangle;
private LegacyRectangle legacyRectangle;
// Constructor for adapting LegacyRectangle to Rectangle
public RectangleAdapter(LegacyRectangle legacyRectangle) {
this.legacyRectangle = legacyRectangle;
}
// Constructor for adapting Rectangle to LegacyRectangle
public RectangleAdapter(Rectangle modernRectangle) {
this.modernRectangle = modernRectangle;
}
// Rectangle methods
@Override
public void drawRectangle() {
if (modernRectangle != null) {
modernRectangle.drawRectangle();
} else {
legacyRectangle.draw();
}
}
@Override
public double getArea() {
if (modernRectangle != null) {
return modernRectangle.getArea();
} else {
return legacyRectangle.calculateArea();
}
}
// LegacyRectangle methods
@Override
public void draw() {
if (legacyRectangle != null) {
legacyRectangle.draw();
} else {
modernRectangle.drawRectangle();
}
}
@Override
public double calculateArea() {
if (legacyRectangle != null) {
return legacyRectangle.calculateArea();
} else {
return modernRectangle.getArea();
}
}
}
// Client code
public class TwoWayAdapterDemo {
public static void main(String[] args) {
// Create objects
Rectangle modernRect = new ModernRectangle(10, 20);
LegacyRectangle oldRect = new OldRectangle(30, 40);
// Use modern rectangle through its native interface
System.out.println("Using modern rectangle directly:");
modernRect.drawRectangle();
System.out.println("Area: " + modernRect.getArea());
// Use legacy rectangle through its native interface
System.out.println("\nUsing legacy rectangle directly:");
oldRect.draw();
System.out.println("Area: " + oldRect.calculateArea());
// Adapt legacy rectangle to modern interface
Rectangle adaptedOldRect = new RectangleAdapter(oldRect);
System.out.println("\nUsing legacy rectangle through modern interface:");
adaptedOldRect.drawRectangle();
System.out.println("Area: " + adaptedOldRect.getArea());
// Adapt modern rectangle to legacy interface
LegacyRectangle adaptedModernRect = new RectangleAdapter(modernRect);
System.out.println("\nUsing modern rectangle through legacy interface:");
adaptedModernRect.draw();
System.out.println("Area: " + adaptedModernRect.calculateArea());
}
}
Real-World Nümunələr
Java I/O Streams
Java I/O Streams, Adapter pattern-in real-world nümunəsidir:
Koda bax
import java.io.*;
public class JavaIOAdapterExample {
public static void main(String[] args) {
try {
// Create a file input stream
InputStream fileInputStream = new FileInputStream("input.txt");
// Adapt file input stream to reader using InputStreamReader
Reader inputStreamReader = new InputStreamReader(fileInputStream);
// Adapt reader to buffered reader
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
// Read from the buffered reader
String line;
while ((line = bufferedReader.readLine()) != null) {
System.out.println(line);
}
// Close resources
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Legacy System Integration
Koda bax
// Modern payment processing interface
interface PaymentProcessor {
void processPayment(double amount);
boolean verifyPayment(String paymentId);
}
// Legacy payment system
class LegacyPaymentSystem {
public void makePayment(float amount, String currency) {
System.out.println("Processing payment of " + amount + " " + currency + " using legacy system");
}
public int checkPaymentStatus(String reference) {
System.out.println("Checking payment status for reference: " + reference);
// 1 = success, 0 = pending, -1 = failed
return 1;
}
}
// Adapter for legacy payment system
class PaymentSystemAdapter implements PaymentProcessor {
private LegacyPaymentSystem legacySystem;
public PaymentSystemAdapter() {
this.legacySystem = new LegacyPaymentSystem();
}
@Override
public void processPayment(double amount) {
// Convert double to float and use default currency
legacySystem.makePayment((float) amount, "USD");
}
@Override
public boolean verifyPayment(String paymentId) {
int status = legacySystem.checkPaymentStatus(paymentId);
return status == 1; // Return true if status is success
}
}
// Modern payment service
class PaymentService {
private PaymentProcessor paymentProcessor;
public PaymentService(PaymentProcessor paymentProcessor) {
this.paymentProcessor = paymentProcessor;
}
public void makePayment(double amount) {
System.out.println("Payment service initiating payment of $" + amount);
paymentProcessor.processPayment(amount);
}
public void verifyPayment(String paymentId) {
boolean isVerified = paymentProcessor.verifyPayment(paymentId);
System.out.println("Payment " + paymentId + " verification result: " + (isVerified ? "Successful" : "Failed"));
}
}
// Client code
public class LegacySystemIntegrationDemo {
public static void main(String[] args) {
// Create adapter for legacy system
PaymentProcessor adapter = new PaymentSystemAdapter();
// Create payment service with the adapter
PaymentService paymentService = new PaymentService(adapter);
// Use the modern payment service which internally uses the legacy system
paymentService.makePayment(100.50);
paymentService.verifyPayment("TX12345");
}
}
Adapter Pattern-nin Üstünlükləri
- Reusability: Mövcud class-ların yenidən istifadəsini təmin edir
- Flexibility: Uyğun olmayan interface-ləri bir-birinə uyğunlaşdırır
- Single Responsibility Principle: Interface çevirmə məsuliyyətini ayrı bir class-a verir
- Open/Closed Principle: Mövcud kodu dəyişdirmədən yeni funksionallıq əlavə etməyə imkan verir
Adapter Pattern-nin Çatışmazlıqları
- Complexity: Əlavə class-lar yaratmaqla kod-un mürəkkəbliyini artırır
- Overhead: Əlavə adapter layer-i performance-a təsir edə bilər
- Multiple Adapters: Çoxlu sayda adapter-lər kod-un başa düşülməsini çətinləşdirə bilər
Adapter Pattern-nin İstifadə Sahələri
- Legacy System Integration: Köhnə sistemləri yeni sistemlərlə inteqrasiya etmək
- Third-party Library Integration: Üçüncü tərəf kitabxanaları mövcud kod-a inteqrasiya etmək
- Interface Standardization: Müxtəlif interface-ləri standartlaşdırmaq
- Code Refactoring: Mövcud kod-u dəyişdirmədən yeni funksionallıq əlavə etmək
Adapter Pattern-nin Digər Pattern-lərlə Müqayisəsi
Adapter vs Bridge
- Adapter: Mövcud class-ların interface-lərini uyğunlaşdırır
- Bridge: Implementation-ı abstraction-dan ayırır
Adapter vs Decorator
- Adapter: Interface-i dəyişdirir
- Decorator: Interface-i dəyişdirmədən funksionallıq əlavə edir
Adapter vs Facade
- Adapter: Bir interface-i başqa bir interface-ə çevirir
- Facade: Mürəkkəb alt sistemlər üçün sadə interface təqdim edir