Introduction
The basic rule for a factory pattern is that the factory should create an instance of an object with a known ancestor, and an unknown implementation behind. Which object to be created is decided by the factory based on the criteria�s passed to the factory�s method that shall instantiate the object.
I would also like to share some rules that I usually follow when I am writing a factory.
- If you need the factory to be able to create more classes than it was originally designed for, you should not have to modify the factory code.
- The factory should be equally unknowing as the caller about the implementation behind the classes that it can instantiate.
- The factory should provide a fixed mechanism how to decide which class to instantiate. The class itself should provide some information to the factory, which the factory should base it�s decision on.
One way solve the issues presented in rules 1 and 2 is to let the factory have an internal list of types it could instantiate, and to provide a register mechanism so that it�s possible to tell the factory which classes it can instantiate.
To solve the third rule it would require the classes which the factory can instantiate to override a static abstract method from the ancestor class, since you need information from the class before it�s instantiated and you need to override it�s behaviour in descendent classes. This is not possible in .NET since there is no meaning by having an static abstract method in .NET, therefore we must solve this with a different approach: Attributes.
In this article I will show you how to implement a factory, which uses attributes to decide which class to instantiate. To fully understand this article you need to have some basic knowledge about attributes.
Background
Why do I think these three rules are so important?
Let's say the factory is part of a plug in architecture and could not be modified because it�s compiled into a host application or a separate DLL. A third party should easily be able to write a plug in without modifying the factory code. Even if the current situation is not requiring this at the moment. If you use these three rules when developing your system this could easily be accomplished if the requirements changes later on
I also strongly believe that a class should be an expert on itself. Since a class obviously knows about it�s own implementation, why shouldn�t the class be a part of the decision making process that decides when to use it? If the class provide the factory with information to base it�s decision on, we could state that the class is an expert on itself since it knows what it does and when to be used.
About the code
To illustrate how to build such a factory, I have decided to use the following case:
The mission is to create an ASP.NET page which says: "Hello World" when a client is requesting the page. The problem is that the page should be available to various types of clients, like HTML browsers, XML Spy and other XML based tools, mobile phones and other WML browsers. The page should also be able to be requested manually by a GET from a telnet client.
Depending on the User Agent, the page should be generated differently, either in HTML, XML, WML or in plain text for the telnet client.
To simplify the demonstration, the application will actually be an ordinary windows application with an input field where to enter the "User Agent", and a textbox to display the output.
The basic idea how to solve this task is to have an abstract class
public abstract class ResponseFormatter
{
public abstract string FormatResponse(string Text);
}
which is inherited by several classes and implemented in different ways. For example one class is implemented to format the text using HTML and another class is formatting the text using WML etc. These classes are the classes that the factory is responsible for instantiating.
The factory class looks like this:
public sealed class ResponseFormatterFactory
{
private ArrayList RegisteredImplementations;
public ResponseFormatterFactory()
{
RegisteredImplementations = new ArrayList();
}
public void RegisterResponseFormatter(Type ResponseFormatterImpl)
{
if (!ResponseFormatterImpl.IsSubclassOf(typeof(ResponseFormatter)))
throw new ResponseFactoryError("Response Formatter
must inherit from class ResponseFormatter");
RegisteredImplementations.Add(ResponseFormatterImpl);
}
public ResponseFormatter CreateResponseFormatter(string UserAgent)
{
...
}
}
To make the factory aware of the classes it can instantiate, it has an ArrayList
that containins the System.Type
for each ResponseFormatter
implementation registered. You must therefor register all implementations with the factory using the method RegisterResponseFormatter
in order to make it aware of them. The method RegisterResponseFormatter
checks that the ResponseFormatter
parameter actually is a type subclassed from the abstract class ResponseFormatter
.
Each class that the factory should manage should be "tagged" with one or more UserAgentAttribute
which looks like this:
[AttributeUsage(AttributeTargets.Class, AllowMultiple=true)]
public class UserAgentAttribute : Attribute
{
private string UserAgentText;
private bool ExactMatch;
public UserAgentAttribute(string UserAgentText, bool ExactMatch)
{
this.UserAgentText = UserAgentText;
this.ExactMatch = ExactMatch;
}
public virtual bool MatchesUserAgent(string UserAgent)
{
if (!ExactMatch)
{
return (UserAgent.ToLower().IndexOf(UserAgentText.ToLower()) != -1);
}
else
return UserAgent.ToLower().Equals(UserAgentText.ToLower());
}
}
The AttributeUsage
attribute says that this attribute is applicable to classes only, and it's okay to use several of these attributes on the same class.
When applying this attribute to a class you must supply two parameters: UserAgentText
and ExactMatch
. The first parameter is the string that is tested with the useragent you supply when you invoke the factory's CreateResponseFormatter
method. The next parameter defines how the match should be performed: exact match (true
) or partial match (false
).
The method MatchesUserAgent
performes the actual test and based on those two parameters it decides whether this class could serve the the specified UserAgent
.
Let's have a look at a class that implements the abstract ResponseFormatter
.
[UserAgent("Mozilla", false)]
public class HTMLResponseFormatter : ResponseFormatter
{
public HTMLResponseFormatter() {}
public override string FormatResponse(string Text)
{
System.Text.StringBuilder Response = new System.Text.StringBuilder();
Response.Append("<html></body>");
Response.Append(Text);
Response.Append("</body></html>");
return Response.ToString();
}
}
First of all, we have the attribute which says that this class should be used when the useragent contains (partial matching) the string "Mozilla".
The method FormatResponse
is overidden to return the string in the variable Text
formatted as HTML.
Now let's check inside the method CreateResponseFormatter
in the ResponseFormatterFactory
class:
public ResponseFormatter CreateResponseFormatter(string UserAgent)
{
foreach (Type impl in RegisteredImplementations)
{
object[] attrlist = impl.GetCustomAttributes(true);
foreach (object attr in attrlist)
{
if (attr is UserAgentAttribute)
{
if (((UserAgentAttribute) attr).MatchesUserAgent(UserAgent))
{
return (ResponseFormatter)
System.Activator.CreateInstance(impl);
}
}
}
}
throw new ResponseFactoryError("Could not find a ResponseFormatter
implementation for this UserAgent");
}
This method consists of two nested loops: the first loop iterate through all the registered types. The second loop iterates through all attributes for the current type. If the attribute is a UserAgentAttribute
it calls the attributes MatchesUserAgent
method to find out if this class could serve the specified UserAgent
. If the MatchesUserAgent
returned true
we simply instantiate the class behind the type using activation and returns it. If the loop's have finished without finding a implementation to use, an exception is raised.
Well let's put everything together and have a look where the factory is used. First we'll create an instance of the factory and register our implementation with it.
factory = new ResponseFormatterFactory();
factory.RegisterResponseFormatter(typeof(HTMLResponseFormatter));
factory.RegisterResponseFormatter(typeof(XMLResponseFormatter));
factory.RegisterResponseFormatter(typeof(TextResponseFormatter));
factory.RegisterResponseFormatter(typeof(WMLResponseFormatter));
Next up is to finally use the factory:
private void btnHello_Click(object sender, System.EventArgs e)
{
try
{
tbResponse.Text =
factory.CreateResponseFormatter(
cobxUserAgent.Text).FormatResponse("Hello World!!");
}
catch (ResponseFactoryError error)
{
MessageBox.Show(error.Message, "Error");
}
}
And that's it!
Points of Interest
Since I am an old Delphi coder I am used to the fact that it's possible to utilize static abstract
methods, or as it's called in Delphi: an abstract class function
. When I found out that this wasn't possible in .NET I was very depressed for a couple of days, until I stumbled upon attributes! Attributes is a very powerful tool for the programmer and could in a way mimic the behaviour of abstract static methods since you can derive attributes as well.
History
- 2003-02-24 -- Initial release
- 2003-02-28 -- Updated