oop:proxy_pattern

Proxy Pattern

Το Proxy Pattern (Μοτίβο Μεσολαβητή) ανήκει στα Δομικά Μοτίβα Σχεδίασης (Structural Design Patterns). Ο κύριος σκοπός του είναι να παρέχει ένα υποκατάστατο ή έναν εκπρόσωπο (proxy) για ένα άλλο αντικείμενο, ώστε να ελέγχει την πρόσβαση σε αυτό.

Σκεφτείτε το σαν μια χρεωστική κάρτα. Η κάρτα είναι ο εκπρόσωπος των μετρητών στο λογαριασμό σας. Όταν πληρώνετε με αυτήν, η κάρτα (proxy) μεσολαβεί για να ελέγξει αν υπάρχει το ποσό και να κάνει τη συναλλαγή με τον πραγματικό τραπεζικό λογαριασμό (real subject).

Η δομή του Proxy Pattern

  • Subject (Διεπαφή/Interface): Ορίζει την κοινή διεπαφή μεταξύ του Client και του Proxy, ώστε ο Proxy να μπορεί να χρησιμοποιηθεί οπουδήποτε αναμένεται το πραγματικό αντικείμενο.
  • RealSubject (Πραγματικό Αντικείμενο): Το πραγματικό αντικείμενο που εκτελεί την ουσιαστική λογική, το οποίο όμως μπορεί να είναι “βαρύ” ή να χρειάζεται έλεγχο πρόσβασης.
  • Proxy (Μεσολαβητής): Διατηρεί μια αναφορά (reference) στο RealSubject. Υλοποιεί την ίδια διεπαφή, ελέγχει την πρόσβαση και προωθεί τα αιτήματα στο RealSubject όταν χρειάζεται.

Περιπτώσεις που χρησιμοποιείται

  • Virtual Proxy (Εικονικός Μεσολαβητής): Όταν το πραγματικό αντικείμενο είναι “βαρύ” (π.χ. φόρτωμα μιας τεράστιας εικόνας ή σύνδεση σε βάση δεδομένων). Ο Proxy καθυστερεί τη δημιουργία του μέχρι να χρειαστεί πραγματικά (Lazy Initialization).
  • Protection Proxy (Μεσολαβητής Προστασίας): Όταν θέλουμε να ελέγξουμε τα δικαιώματα πρόσβασης (Authorization) ενός χρήστη πριν του επιτρέψουμε να καλέσει μια μέθοδο.
  • Remote Proxy (Απομακρυσμένος Μεσολαβητής): Όταν το πραγματικό αντικείμενο βρίσκεται σε διαφορετικό reference space (π.χ. σε άλλον server) και ο Proxy αναλαμβάνει τη δικτυακή επικοινωνία (RMI, RPC).
  • Logging/Caching Proxy: Για να κρατάμε ιστορικό (logs) των κλήσεων ή για να αποθηκεύουμε αποτελέσματα (cache) ώστε να μην ξαναεκτελείται μια κοστοβόρα λειτουργία.

Παράδειγμα - Caching Proxy

Θα περιγράψουμε το κλασσικό και χρήσιμο παράδειγμα του Proxy caching μηχανισμού μιας βάσης δεδομένων. Σε αυτό το σενάριο, ο Proxy μεσολαβεί ανάμεσα στον Client και την πραγματική Βάση Δεδομένων (RealDatabase). Διατηρεί στη μνήμη του έναν HashMap που λειτουργεί ως Cache. Ακολουθεί η σχηματική υλοποίηση σε Java.

Το Interface (Subject)

Το interface ορίζει την κοινή λειτουργία αναζήτησης δεδομένων.

Database.java
public interface Database {
    String query(String sqlQuery);
}

Η Bάση Δεδομένων (RealSubject)

Προσομοιώνει μια “βαριά” βάση δεδομένων που χρειάζεται χρόνο για να απαντήσει (π.χ. λόγω δίσκου ή δικτύου).

RealDatabase.java
public class RealDatabase implements Database {
    @Override
    public String query(String sqlQuery) {
        // Προσομοίωση καθυστέρησης της πραγματικής βάσης
        System.out.println("[RealDB] Searching on database for: \"" + sqlQuery + "\"...");
        try {
            Thread.sleep(1500); // Καθυστέρηση 1.5 δευτερολέπτου
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
 
        // Επιστροφή υποτιθέμενων δεδομένων ανάλογα με το ερώτημα
        if (sqlQuery.contains("id = 1")) {
            return "{id: 1, name: 'Mickey Mouse'}";
        }
        return "{No Results found!}";
    }
}

H Proxy Cache Database

Αυτή η κλάση έχει ως πεδίο την πραγματική βάση δεδομένων και ένα HashMap για την προσωρινή αποθήκευση των αποτελεσμάτων (μηχανισμός cache).

ProxyCacheDatabase.java
import java.util.HashMap;
import java.util.Map;
 
public class ProxyCacheDatabase implements Database {
    // Ο Proxy έχει ως πεδίο την πραγματική βάση
    private RealDatabase realDatabase;
    // Η μνήμη Cache για γρήγορη ανάκτηση
    private Map<String, String> cache;
 
    public ProxyCacheDatabase() {
        this.realDatabase = new RealDatabase();
        this.cache = new HashMap<>();
    }
 
    @Override
    public String query(String sqlQuery) {
        System.out.println("[ProxyCache] Checking cache first...");
 
        // 1. Αν το αποτέλεσμα υπάρχει στην Cache, το επιστρέφει ΚΑΤΕΥΘΕΙΑΝ
        if (cache.containsKey(sqlQuery)) {
            System.out.println("[ProxyCache] HIT! Found in Cache.");
            return cache.get(sqlQuery);
        }
 
        // 2. Αν ΔΕΝ υπάρχει στην Cache (Cache Miss)
        System.out.println("[ProxyCache] MISS! No data found in Cache. Connecting to RealDB...");
 
        // Ρωτάει την πραγματική βάση
        String result = realDatabase.query(sqlQuery);
 
        // 3. Αποθηκεύει το νέο αποτέλεσμα στην Cache για το επόμενο request
        cache.put(sqlQuery, result);
        System.out.println("[ProxyCache] Updated cache.");
 
        return result;
    }
}

O Client

Θα κάνουμε το ίδιο ερώτημα δύο φορές για να δούμε τη διαφορά στην ταχύτητα.

ClientDB.java
public class ClientDB {
    public static void main(String[] args) {
        // Ο Client χρησιμοποιεί τη βάση μέσω του Caching Proxy
        Database db = new ProxyCacheDatabase();
        String queryStr = "SELECT * FROM users WHERE id = 1";
 
        // 1η Κλήση: Η cache είναι άδεια, θα πάει στη RealDB (Θα κάνει 1.5 δευτερόλεπτο)
        System.out.println("\n=== 1st Attempt (Empty Cache) ===");
        long startTime = System.currentTimeMillis();
        String result1 = db.query(queryStr);
        long endTime = System.currentTimeMillis();
        System.out.println("Result: " + result1);
        System.out.println("Exec time: " + (endTime - startTime) + " ms\n");
 
        // 2η Κλήση: Τα δεδομένα υπάρχουν ήδη, θα απαντήσει ο Proxy ακαριαία (0 ms)
        System.out.println("=== 2nd Attempt (Found in Cache) ===");
        startTime = System.currentTimeMillis();
        String result2 = db.query(queryStr);
        endTime = System.currentTimeMillis();
        System.out.println("Result: " + result2);
        System.out.println("Exec time: " + (endTime - startTime) + " ms");
    }
}
  • Διαφάνεια: Ο Client καλεί απλά την db.query(). Δεν τον νοιάζει αν τα δεδομένα ήρθαν από τη μνήμη RAM του Proxy ή από τον σκληρό δίσκο της RealDB.
  • Έλεγχος Ροής: Ο Proxy προστατεύει τη βαριά βάση από το να δέχεται συνεχώς τα ίδια “κουραστικά” ερωτήματα, βελτιώνοντας δραματικά την ταχύτητα της εφαρμογής.
oop/proxy_pattern.txt · Last modified: 2026/05/25 10:55 by gthanos