The idea of the Proxy design pattern is to provide a substitute for another object were the substitute controls the access to the other project. So there is one real subject with a given interface, for example an object to access a database. And there is a proxy which maintains a reference to this real subject. They both implement the same interface. So the proxy can be used as substitute of the real subject. Often the client does not even know that he uses a proxy.
The proxy controls the access to the real subject and will be responsible for additional tasks, for example creation or deleting of the real subject. The proxy may add some new functionality for example logging, caching, queuing, remote execution or access control.
The following example shows a possible implementation of the Proxy design pattern. Within the example we want to create a class which sends a message to one or some remote listeners. Sending the message may take a while. Therefore the send method will block the client or the client hast to implement asynchronous method calls. Therefore we want to provide a proxy which is responsible for queuing and sending messages. The proxy shall offer the same functionality like the real message provider extended with a queuing mechanism and asynchronous execution of the remote data transfer.
At first we will define the interface for the message channel.
public interface IMessageChannel { void SendMessage(string message); }
Now we will implement an according class. To simulate the slow remote execution we add a timeout of one second.
public class MessageChannel : IMessageChannel { public void SendMessage(string message) { Thread.Sleep(TimeSpan.FromSeconds(1)); } }
At next we can create the queued message channel which is a proxy of the real message channel. Therefore it holds an instance of the real object and it implements the same interface. To keep it simple we don’t implement the parallel task which executes the sending process. Instead we just add a DoWork method as example of the content of the parallel task.
public class QueuedMessageChannel : IMessageChannel { Queue<string> _messages = new Queue<string>(); IMessageChannel _messageChannel = new MessageChannel(); public void SendMessage(string message) { _messages.Enqueue(message); } /// <summary> /// send the messages /// TODO: this must be executed as loop inside a parallel task /// </summary> private void DoWork() { string message; if (_messages.Count > 0) { message = _messages.Dequeue(); _messageChannel.SendMessage(message); } } }
Within a console application we can new use the proxy. In this example the client creates the proxy explicitly. If you use a factory instead, the console application will not even know whether it uses a proxy or not. But on execution of the send method you will see the difference as the proxy method will return immediately.
static void Main(string[] args) { IMessageChannel messageChannel; messageChannel = new QueuedMessageChannel(); messageChannel.SendMessage("abc"); Console.WriteLine("Message was send"); messageChannel.SendMessage("abc"); Console.WriteLine("Message was send"); messageChannel.SendMessage("abc"); Console.WriteLine("Message was send"); Console.ReadKey(); }