Wzorzec Proxy należy do wzorców strukturalnych, jest często wykorzystywany by odciążyć klasę obsługującą wolne źródło np. system bazodanowy, także by stworzyć warstwę bezpieczeństwa pomiędzy dwiema warstwami w programowaniu zorientowanym obiektowo kontrolując zasady dostępu, albo także w zarządzaniu transakcjami. Jest swoistym interfejsem i z zasady posiada dokładnie taki sam interfejs jak obiekt klasy, którego jest pełnomocnikiem (proxy). Z tego powodu jest podobny do dwu innych wzorców tj. Fasady (Facade) oraz Adaptera, ale pełni funkcje dla nieco innych zastosowań. Pełnomocnik pozwala na dodanie funkcjonalności obiektowi już istniejącej klasy bez konieczności zmiany kodu klienta.

Po co Ci on?

Klasycznym chyba już przykładem zastosowania tego wzorca jest obsługa zapytań do bazy danych. Zazwyczaj duże bazy mają zasoby przechowywane w pamięci masowej i wymagają stosunkowo długiego czasu aby ją uruchomić i wyselekcjonować żądane dane. Istniejący pełnomocnik zajmuje się obsługą żądań ze strony klienta w czasie, gdy baza jest jeszcze nie gotowa (np. trwa inicjalizacja usługi). Może także dla najczęściej zadawanych zapytań prowadzić pamięć podręczną (cache) i pomijając wolne źródło zwracać odpowiedź na zapytanie w znacznie krótszym czasie.

Jak się podpiąć?

Wpierw możemy skonstruować podstawę naszego proxy w postaci definicji wspólnego interfejsu:

package wzorceprojektowe1;

interface NaszInterfejs {
    boolean test();
}

Teraz już mając zaczątek możemy skonstruować klasę “obsługującą” wolne źródło. Oczywiście w kodze takiego źródła nie ma, bo dla demonstracji Proxy nie jest ta implementacja potrzebna, a dodatkowo przysłoniłaby ona sedno działania pełnomocnika. Dlatego też funkcja test() jest metodą dedykowaną jako demonstrator: “wolny dostęp”.

class GlownyObiekt implements NaszInterfejs {

        @Override
        public boolean test() {
           System.out.println("Tu jestem");
           return true;
        }
    }

Nadszedł już czas aby zainstalować pełnomocnika dla naszej “wolnej” klasy…

public class ProxyService implements NaszInterfejs {
    public GlownyObiekt go = new GlownyObiekt();
    @Override
    public boolean test() {
        return go.test();
        
    }
}

Na koniec pozostało już tylko pokazać jak użyć powyższej struktury…

public static void main(String[] args) {
  
    ProxyService proxy=new ProxyService();
    proxy.test();
}           

Zadanie: Przygotuj program obsługi prostej bazy danych (min. 20 rekordów, 5 atrybutów itp) w pliku binarnym. Zaimplementuj klasę obsługującą wyszukiwanie w takiej bazie z wykorzystaniem archetypów składni SQL (prosty select, insert itp.1czy nie nasuwa Ci się automatycznie skojarzenie, że w takiej sytuacji powinieneś użyć jeszcze innego wzorca projektowego? Jakiego? Jakich?).

Pokaż jak działa wyszukiwanie np. po jakimś atrybucie z mierzeniem czasu zapytania z wykorzystaniem klasy podstawowo obsługującej tę bazę danych.

Zaprojektuj i wykonaj klasę Proxy z 10-elementową pamięcią podręczną dla zapytań do bazy danych. Pokaż jak działa wykorzystanie klasy Proxy dla tej bazy danych pokazując czas wyszukiwania dla nowego zapytania do bazy oraz zapytania, które pojawiło się już w ostatnich 10 zapytaniach.