Pełnomocnik (ang. Proxy) jest wzorcem, który pozwala na zbudowanie warstwy pośredniczącej między obiektem a innymi obiektami, które z niego korzystają. Przykładowo może służyć on do kontrolowania dostępu do obiektu, lub do utworzenia go dopiero w momencie, gdy będzie on używany.
Pełnomocnik należy do strukturalnych wzorców projektowych. Więcej o rodzajach wzorców można poczytać tutaj [under construction].
Przykład 1:
Rozważmy aplikację, która ładuje obrazek z pamięci i wyświetla go na ekran:
interface IImageDisplayer { void Display(); } class ImageDisplayer : IImageDisplayer { private string _path; private Image _image; public ImageDisplayer(string path) { _path = path; _image = LoadImage(_path); } private Image LoadImage(string path) { //ładujemy obrazek z dysku return new Image(); } public void Display() { Console.WriteLine("Wyświetlam obrazek."); } } class Image { }
Klasa ImageDisplayer nie jest dobra: ładuje obrazek z pamięci już w momencie utworzenia instancji klasy. Możemy wyobrazić sobie sytuację, gdy nie jest nam to na rękę:
class Program { static void Main() { var imageDisplayer = new ImageDisplayer("veryBigImage.png"); var random = new Random(); if (random.Next() % 2 == 0) { imageDisplayer.Display(); } Console.ReadKey(); } }
W powyższym programie obrazek może się wyświetlić, lub nie - jest na to pięćdziesięcioprocentowa szansa. Jednak obrazek - tak czy inaczej - zostanie załadowany z dysku. Nie jest to optymalne. Po co w ogóle go ładować, jeśli ma się nie wyświetlić?
Moglibyśmy po prostu zrefaktorować klasę ImageDisplayer. Wyobraźmy sobie jednak, że z jakichś przyczyn nie możemy lub nie chcemy tego robić. Na pomoc przychodzi nam wzorzec Pełnomocnik:
class ImageDisplayerProxy : IImageDisplayer { private ImageDisplayer _imageDisplayer; private string _path; public ImageDisplayerProxy(string path) { _path = path; } public void Display() { if(_imageDisplayer == null) { _imageDisplayer = new ImageDisplayer(_path); } _imageDisplayer.Display(); } }
Jak widzimy, Pełnomocnik implementuje ten sam interfejs co ImageDisplayer. Przechowuje w sobie referencję do obiektu klasy ImageDisplayer, ale utworzy ten obiekt dopiero, gdy faktycznie zajdzie potrzeba wyświetlenia obrazka na ekran. Dzięki temu nie będziemy bezsensownie ładować obrazka do pamięci, jeśli nie będzie takiej potrzeby.
Przykład 2:
Załóżmy, że mamy w naszym systemie klasę służącą do dokonywania płatności online:
interface IPayer { void Pay(decimal amount); } class Payer : IPayer { public void Pay(decimal amount) { Console.WriteLine($"Płacę kwotę {amount}"); } }
Z czasem pojawia się potrzeba, by ograniczyć możliwość dokonywania płatności tylko do tych użytkowników klasy, którzy znają hasło. Możemy utworzyć Pełnomocnika, który ograniczy dostęp do klasy Payer:
class PayerWithPasswordProxy : IPayer { private const string _correctPassword = "1234"; private string _password; private Payer _payer; public PayerWithPasswordProxy(string password) { _payer = new Payer(); _password = password; } public void Pay(decimal amount) { if(_password == _correctPassword) { _payer.Pay(amount); } else { Console.WriteLine("Błędne hasło."); } } }
Jak widzimy, Pełnomocnik pozwolił nam ograniczyć dostęp do metody Pay.
Pełnomocnik a Dekorator:
Jak widać po powyższych przykładach, Pełnomocnik jest wzorcem dość podobnym do Dekoratora. Przyjrzyjmy się różnicom:
- Pełnomocnika używamy by w jakiś sposób ograniczyć dostęp do danego obiektu. Dekoratora - by dynamicznie dodać nową funkcjonalność do istniejących klas
- Relację między Pełnomocnikiem a klasą do której dostęp ogranicza ustalamy w czasie kompilacji. Relacje Dekoratora są dynamiczne i ustalane w czasie wykonywania programu
- Dekorator zawsze zawiera referencję do istniejącego dekorowanego obiektu. W przypadku Pełnomocnika obiekt ten może nigdy nie zostać utworzony (jak w przykładzie 1)
W tym poście korzystałam z:
- https://www.tutorialspoint.com/design_pattern/proxy_pattern.htm
- https://stackoverflow.com/questions/18618779/differences-between-proxy-and-decorator-pattern
- https://lukasz-socha.pl/php/wzorce-projektowe-cz-12-proxy/
Komentarze
Prześlij komentarz