.NET i takie tam

Dynamic Proxy

with one comment

Jednym z popularnych wzorców projektowych jest wzorzec Proxy. W skrócie, obiekt pełniący rolę proxy, implementuje interfejsy i zarządza, zgodnie z jakąś logiką, wywołaniami metod jakiejś innej klasy. Przykładowo, załóżmy, że mamy następujące klasy:

Kod:

public interface IMessageSender
{
    void Send(string message);
    void Send();
}

public class SimpleSender : IMessageSender
{
    public virtual void Send(string message)
    {
        Console.WriteLine("Send() - with praram: " + message);
    }
    
    public virtual void Send()
    {
        Console.WriteLine("Send() - no praram");
    }
}

public class Sender : IMessageSender
{
    private readonly IMessageSender _sender;
    
    public Sender(IMessageSender sender)
    {
        _sender = sender;
    }
    
    public void Send(string message)
    {
        // tutaj implementujemy dodatkową logikę sterującą wysyłaniem wiadomości
        Console.WriteLine("Send action controlled by Sender object.");
        _sender.Send(message);
    }
    
    public void Send()
    {
        // tutaj implementujemy dodatkową logikę sterującą wysyłaniem wiadomości
        Console.WriteLine("Send action controlled by Sender object.");
        _sender.Send();
    }
}

W powyższym przykładzie Sender jest klasą proxy dla klasy SimpleSender. Obie klasy implementują interfejs IMessageSender, przy czym tylko klasa SimpleSender ‚wie’ jak wysłać wiadomość. Obiekt proxy (Sender) implementuje dodatkową logikę sterującą wywołaniem poszczególnych metod klasy SimpleSender. Przykładowe wywołanie metody Send():

Sender proxy = new Sender(new SimpleSender());
proxy.Send();

Istnieje możliwość automatycznego generowania klas proxy w trakcie działania aplikacji – zwana Dynamic Proxy. Przy korzystaniu z tej techniki zwykle korzysta się z dodatkowych bibliotek jak Castle DynamicProxy czy LinFu.DynamicProxy. W poniższym przykładzie wykorzystałem tą ostatnią bibliotekę:

using LinFu.DynamicProxy;

public interface IMessageSender
{
    void Send(string message);
    void Send();
}

public class SimpleSender : IMessageSender
{
    public virtual void Send(string message)
    {
        Console.WriteLine("Send() - with praram: " + message);
    }
    
    public virtual void Send()
    {
       Console.WriteLine("Send() - no praram");
    }
}

public class MessageSenderInterceptor : IInvokeWrapper
{
    private readonly IMessageSender _sender;
    
    public MessageSenderInterceptor(IMessageSender sender)
    {
       _sender = sender;
    }
    
    public void BeforeInvoke(InvocationInfo info)
    {
       Console.WriteLine("We are about to call method: " + info.TargetMethod);
    }
    
    public object DoInvoke(InvocationInfo info)
    {
       return info.TargetMethod.Invoke(_sender, info.Arguments);
    }
    
    public void AfterInvoke(InvocationInfo info, object returnValue)
    {
    }
}

IMessageSender i SimpleSender są dokładnie takie same jak w poprzednim przykładzie, nowością jest klasa MessageSenderInterceptor implementująca interfejs IInvokeWrapper. Jak widać z implementacji definiuje on metody określające co się stanie przed, w trakcie i po tym jak w kodzie wywołamy metody zawarte w naszym bazowym interfejsie IMessageSender. Na koniec, sposób wywołanie metody Send():

var proxy = new ProxyFactory();
var senderInterceptor = new MessageSenderInterceptor(new SimpleSender());
var finalSender = proxy.CreateProxy<IMessageSender>(senderInterceptor);
finalSender.Send("Hello World!");

Wszystko prosto ładnie, tylko nasuwa się pytanie po co? Po co dodawać dodatkowe referencje do nowych bibliotek, po co korzystać z ProxyFactory i innych interfejsów, skoro można napisać to wszystko prosto samemu?

Fakt jeśli mamy jedną, dwie klasy implementujące interfejs IMessageSender których zachowanie chcielibyśmy w jakiś sposób zmodyfikować – to jasne, nie ma sensu bawić się w Dynamic Proxy.

Jeżeli natomiast klas tych jest więcej Dynamic Proxy zaoszczędzi nam sporo fatygi w implementowaniu obiektów proxy dla poszczególnych klas implementujących IMessageSender, nie mówiąc już o mniejszej ilości klas do przetestowania. Co więcej zastosowanie dowolnego Dependency Injection Framework pozwoli nam uczynić Dynamic Proxy niewidoczne dla programisty korzystającego z naszego kodu.

Written by sakowicz

Grudzień 16, 2008 @ 1:52 pm

Jedna odpowiedź

Subscribe to comments with RSS.

  1. Właśnie wczoraj stworzyłem interfejs podobny do IMessageSender oraz klasę podobną do Sender i nie wiedziałem, że to co stworzyłem to proxy. Od teraz mogę mówić, że mój komponent korzysta z wzorca proxy…🙂

    Michal

    Luty 27, 2009 at 7:53 am


Skomentuj

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Log Out / Zmień )

Zdjęcie z Twittera

Komentujesz korzystając z konta Twitter. Log Out / Zmień )

Facebook photo

Komentujesz korzystając z konta Facebook. Log Out / Zmień )

Google+ photo

Komentujesz korzystając z konta Google+. Log Out / Zmień )

Connecting to %s

%d bloggers like this: