User Tools

Site Tools


oop:adapter_pattern

This is an old revision of the document!


Adapter Pattern

Το Adapter Pattern (Μοτίβο Προσαρμογέα) ανήκει στα δομικά μοτίβα σχεδίασης (Structural Design Patterns). Η κύρια λειτουργία του είναι να επιτρέπει σε δύο μη συμβατά interfaces (διεπαφές) να συνεργαστούν μεταξύ τους. Μπορείτε να το σκεφτείτε σαν έναν οποιονδήποτε προσαρμογέα ή μετασχηματιστή.

Πότε το χρησιμοποιούμε;

Όταν θέλουμε να χρησιμοποιήσουμε μια υπάρχουσα κλάση, αλλά το interface της δεν ταιριάζει με το υπόλοιπο σύστημα. Όταν δημιουργούμε ένα επαναχρησιμοποιήσιμο component που πρέπει να συνεργαστεί με μελλοντικές ή απρόβλεπτες κλάσεις που δεν μοιράζονται το ίδιο interface. Συνήθως χρησιμοποιείται κατά την ενσωμάτωση legacy κώδικα (παλιού κώδικα) ή βιβλιοθηκών τρίτων (3rd party libraries).

Τα βασικά συστατικά:

  • Target (Στόχος): Το interface που περιμένει και καταλαβαίνει ο Client (ο τρέχων κώδικάς μας).
  • Client (Πελάτης): Η κλάση που χρησιμοποιεί το Target interface.
  • Adaptee (Προσαρμοζόμενος): Η υπάρχουσα κλάση/βιβλιοθήκη που έχει το ασύμβατο interface, αλλά περιέχει τη συμπεριφορά που χρειαζόμαστε.
  • Adapter (Προσαρμογέας): Η κλάση που υλοποιεί το Target interface και εσωτερικά κρατάει μια αναφορά (reference) στον Adaptee, μεταφράζοντας τις κλήσεις.

Παράδειγμα - Σύνδεση με σύστημα πληρωμών

Ας υποθέσουμε ότι έχουμε ένα σύστημα πληρωμών που λειτουργεί μέσω ενός interface που ονομάζεται PaymentProcessor και δέχεται πληρωμές σε ευρώ. Θέλουμε να ενσωματώσουμε μια νέα υπηρεσία πληρωμών (έστω την “X-Pay”), η οποία δέχεται ποσά μόνο σε Δολλάρια ($) και οι μέθοδοί της έχουν διαφορετικά ονόματα. Θα φτιάξουμε έναν Adapter που θα αναλάβει και τη μετάφραση των μεθόδων, αλλά και τη μετατροπή του νομίσματος.

PaymentProcessor.java
// Target Interface (Λειτουργεί με Ευρώ)
public interface PaymentProcessor {
    void processPayment(double amountInEuro);
}

Η κλάση X-Pay την οποία θέλουμε να προσαρμόσουμε:

XPayGateway.java
// Adaptee Class (Λειτουργεί με Δολλάρια)
public class XPayGateway {
    public void chargeInUSD(double amountInUSD) {
        System.out.println("Η X-Pay χρέωσε επιτυχώς: $" + amountInUSD);
    }
}

Ο προσαρμογέας υλοποιεί το PaymentProcessor (άρα το E-shop τον βλέπει σαν κανονικό processor), αλλά εσωτερικά καλεί την XPayGateway, αφού πρώτα μετατρέψει τα Ευρώ σε Δολλάρια.

XPayAdapter.java
// Adapter
public class XPayAdapter implements PaymentProcessor {
    private XPayGateway xPayGateway;
    private static double euro2usd; // Ισοτιμία
 
    public XPayAdapter(XPayGateway xPayGateway, double conversion_rate) {
        this.xPayGateway = xPayGateway;
        euro2usd = conversion_rate;
    }
 
    @Override
    public void processPayment(double amountInEuro) {
        // 1. Μετατροπή του ποσού από Ευρώ σε Δολλάρια
        double amountInUSD = amountInEuro * euro2usd;
 
        System.out.printf("Adapter: Μετατροπή %.2f€ σε %.2f$%n", amountInEuro, amountInUSD);
 
        // 2. Κλήση της μεθόδου του Adaptee
        xPayGateway.chargeInUSD(amountInUSD);
    }
}

Τελικά με τη χρήση του Adapter το σύστημα πληρωμών μπορεί να επωφεληθεί από την εν λόγω υπηρεσία.

PaymentService.java
public class PaymentService {
    public static void main(String[] args) {
        // 1. Ο πελάτης αγοράζει κάτι που κοστίζει 100 Ευρώ
        double orderTotal = 100.00;
 
        // 2. Δημιουργούμε το instance της ξένης βιβλιοθήκης (Adaptee)
        XPayGateway xPaymentService = new XPayGateway();
 
        // 3. "Κλειδώνουμε" τη βιβλιοθήκη μέσα στον Adapter
        PaymentProcessor paymentProcessor = new XPayAdapter(xPaymentService);
 
        // 4. Το E-shop εκτελεί την πληρωμή κανονικά σε Ευρώ, χωρίς να ξέρει τι γίνεται από πίσω
        System.out.println("E-shop: Εκκίνηση πληρωμής για την παραγγελία...");
        paymentProcessor.processPayment(orderTotal);
    }
}

Κλάσεις της βασικής βιβλιοθήκης που υλοποιούν το Adapter Pattern

Το Adapter Pattern είναι εξαιρετικά δημοφιλές και η ίδια η Java το χρησιμοποιεί εκτενώς στις δικές της ενσωματωμένες βιβλιοθήκες (JDK). Όποτε βλέπετε μια κλάση που παίρνει ένα αντικείμενο ενός τύπου και το “μεταμορφώνει” ώστε να μπορεί να χρησιμοποιηθεί από ένα API που απαιτεί έναν άλλον τύπο, έχεις να κάνεις με έναν Adapter. Ακολουθούν κάποια κλασικά παραδείγματα:

java.util.Arrays#asList()

  • Το πρόβλημα: Έχεις έναν κλασικό πίνακα (Array), αλλά θέλεις να τον περάσεις σε μια μέθοδο που δέχεται μια λίστα (List), για να μπορείς να χρησιμοποιήσεις μεθόδους των Collections (όπως αναζήτηση, streams κλπ).
  • Η λύση (Adapter): Η μέθοδος Arrays.asList() λειτουργεί ως προσαρμογέας. Παίρνει τον πίνακα και επιστρέφει μια List η οποία “κάθεται” πάνω από τον πίνακα. Κάθε αλλαγή στη λίστα επηρεάζει τον πίνακα, επειδή η λίστα είναι απλώς ένα “προσωρείο” (interface adapter) πάνω από τον πίνακα.
oop/adapter_pattern.1779656364.txt.gz · Last modified: 2026/05/24 20:59 by gthanos