Introduction
This series of articles will attempt to demonstrate the usage of all the design patterns. This article begins with Proxy. The series wont follow the GoF order of design patterns. Firstly, I will try to post, those that have been rated as more frequent in use, according to http://www.dofactory.com/
Background
Books along with a vast variety of online articles are a great source to explain design patterns. However, my inspiration, came when few months ago, a colleague of mine, made a good point. He pointed, that, despite the fact that he often red many of them, he still couldn’t really grasp, how to implement them, in the business code base, where we are required, to implement IoC containers and Unit testing. So, the purpose of the series, IS NOT to explain the patterns, as there are many books and articles available, BUT to demonstrate the usage of design patterns, along with tools that most of the developers use in their working environment.
The series will begin with Proxy design pattern, the folowing is based on the Spacebook example, taken from C# 3.0 Design patterns. I have tried to refactor the code in the book and show an alternative version, by using Castle.Windsor and NUnit framework.
The author of the SpaceBook example, found in the book, uses a private class as the subject class, as he tried to give emphasis on the following feature in Proxy "Client shouldn’t be able to access The Subject class. Access to the Subject class is enabled through the proxy".
A brief analysis regarding Proxy, which is available in the C# 3.0 Design patterns book:
The players in the pattern are:
ISubject
A common interface for subjects and proxies that enables them to be used
interchangeably
Subject
The class that a proxy represents
Proxy
A class that creates, controls, enhances, and authenticates access to a Subject
Request
An operation on the Subject that is routed via a proxy
The central class, Proxy, implements the ISubject interface. The Client can use
ISubject to abstract away from proxies.
Each Proxy object maintains a reference to a Subject, which is where the action really takes place.
The Proxy performs frontend work. Its value can be enhanced by making the Subject a private
class so that the Client cannot get to the Subject except via the Proxy.
The key features of the “refactored” version of the Spacebook articles is:
- Hides the Subjest class, from the client by making it internal.
- Subject class inherits ISpaceBook
- Internal classes can be unit tested
- Dependency Injection using Castle.Windsor
- Subject and Proxy class, can be both Unit tested
Using the code
The solution is constitued by the bollowing projects:
- Client project (will be calling the Proxy Class NOT the subject class)
- ProxyCastle projects (Subject and proxy class)
- ProxyCastle.UnitTests
- ProxyCastle.Dependencies
First step is to create the ISpaceBook interface:
public interface ISpaceBook
{
bool IsUnique(string name);
void Add(string n);
void Add(string friend, string message);
void Poke(string who, string friend);
string SetName { set; }
}
Then, we create the internal SpaceBook class that implements ISpaceBook interface. As mentioned before, one of the features, surrounding, proxy design pattern, is the fact that Client should not be able to access Subject class. Thats the whole point of having a proxy class, so that the access can be achieved through the surrogated proxy class. , the Spacebook example, in the book , hides the Subject class from the Client, by making it Private. I have tried to achieve the same , by making the class Internal.
internal class SpaceBook :ISpaceBook
{
internal static SortedList<string, SpaceBook> community = new SortedList<string, SpaceBook>(100);
private string pages;
private string name;
private string gap = "\n\t\t\t\t";
public string SetName
{
set { name = value; community[name] = this; }
}
public bool IsUnique(string name)
{
return community.ContainsKey(name);
}
public virtual void Add(string s)
{
pages += gap + s;
Console.Write(gap + "======== " + name + "'s SpaceBook =========");
Console.Write(pages);
Console.WriteLine(gap + "===================================");
}
public virtual void Add(string friend, string message)
{
community[friend].Add(message);
}
public void Poke(string who, string friend)
{
community[who].pages += gap + friend + " poked you";
}
}
In the AssemplyInfo.cs , we will need to add the following so that we can enable the internal module to be "visible" in the projects WE only want it to be visible:
[assembly: InternalsVisibleTo("ProxyCastle.UnitTests",AllInternalsVisible = true)]
[assembly: InternalsVisibleTo("ProxyCastle.Dependencies", AllInternalsVisible = true)]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
And then we can finally create the Proxy class:
public class SpaceBookProxy
{
private ISpaceBook _spaceBook;
private string password;
private string name;
public bool loggedIn = false;
public SpaceBookProxy(ISpaceBook spacebook)
{
if (spacebook == null)
throw new ArgumentNullException("spaceBook");
if (_spaceBook == null)
_spaceBook = spacebook;
}
private void Register()
{
Console.WriteLine("Let's register you for SpaceBook");
do
{
Console.WriteLine("All SpaceBook names must be unique");
Console.Write("Type in a user name: ");
name = Console.ReadLine();
} while (_spaceBook.IsUnique(name));
Console.Write("Type in a password: ");
password = Console.ReadLine();
Console.WriteLine("Thanks for registering with SpaceBook");
}
private bool Authenticate()
{
Console.Write("Welcome " + name + ". Please type in your password: ");
string supplied = Console.ReadLine();
if (supplied == password)
{
loggedIn = true;
Console.WriteLine("Logged into SpaceBook");
_spaceBook.SetName = name;
return true;
}
Console.WriteLine("Incorrect password");
return false;
}
public void Add(string message)
{
Check();
if (loggedIn)
_spaceBook.Add(message);
}
public void Add(string friend, string message)
{
Check();
if (loggedIn)
_spaceBook.Add(friend, name + " said: " + message);
}
public void Poke(string who)
{
Check();
if (loggedIn)
_spaceBook.Poke(who, name);
}
private void Check()
{
if (!loggedIn)
{
if (password == null)
Register();
Authenticate();
}
}
public bool IsUnique(string name)
{
return _spaceBook.IsUnique(name);
}
public void Poke(string who, string friend)
{
_spaceBook.Poke(who, friend);
}
}
At this point, we introduced the interface on the Subject class level, so that the Proxy can refer to the Subject , through the interface using dependency injection. The Client app , will be calling the Proxy Class, without knowing the existence of the Subject class. (SpaceBook)
namespace Client
{
class Program
{
public static void Main()
{
var mySpaceBook = new SpaceBookProxy(DependencyResolver.Disolve());
mySpaceBook.Add("Hello world");
mySpaceBook.Add("Today I worked 18 hours");
var tom = new SpaceBookProxy(DependencyResolver.Disolve());
tom.Poke("Veronica");
tom.Add("Veronica", "Poor you");
tom.Add("Off to see the Lion King tonight");
}
}
}
The last step is to add the ProxyCastle.Dependencies project :
We could have delegated this responsibility to the Client, but then we would need to make the Subject class, available in the Client.The Client would need "to know" about the Subject class, if we wanted to resolve the dependencies at this level.
To Solve that, we could delegate this responsibility, to another project . This is where we add ProxyCastle.Dependencies. This project would need to know about the Subject class, so we choose to resolve dependencies on this project. With this way, the Client can maintain the structure we wanted, by referring to the Subject class THROUGH the proxy and therefore, it doesn't need to know about the SpaceBook class.
namespace ProxyCastle.Dependencies
{
public class DependencyResolver
{
private static WindsorContainer _container;
public static ISpaceBook Resolve()
{
_container = new WindsorContainer();
_container.Register(Component.For<ISpaceBook>().ImplementedBy<SpaceBook>().LifeStyle.Transient);
return _container.Resolve<ISpaceBook>();
}
}
}
I have included the solution to this article. If there is anything that could be better improved, please let me know and I will update accordingly.
Points of Interest
Internal classes and Unit testing
Dependency Injection using Castle.Windsor
History
Keep a running update of any changes or improvements you've made here.