Using the Factory Pattern
Software Development December 4th, 2007A friend of mine asked me an interesting question the other day. He wanted some help coming up with a solution to a problem he was running into. Here is the short version of the problem requirements:
- He had an abstract class and several derived classes.
- His derived classes were to be serialized as XML on the file system, and he wanted to instantiate them at run time.
- He wouldn’t know until run time which derived class he was dealing with.
He was using C++ and he eventually came up with a good solution to the problem, but it got me wondering how I would handle the same requirements using C#. This sounded like a tailor made case for the factory design pattern, so I decided to give it a shot and see where it led.
The factory design pattern is a creational design pattern, which basically means that it deals with the creation of objects. It allows you to create an object at design time without really knowing what type of object you are really dealing with. Confused? How about a simple example.
1: // We have an abstract class Product
2: // that has 2 derived classes --
3: // ProductA and ProductB
4: public abstract class Product{}
5: public class ProductA : Product{}
6: public class ProductB : Product{}
7:
8: // If we do it this way at design time
9: // we can't change the type of object
10: // instantiated without recompiling the application.
11: // The variable prod will always be of type ProductA
12: ProductA prod = new ProductA();
I want to avoid making the decision about what time of object to instantiate until run time, which is where the factory pattern comes in. Another of our requirements is that the objects be serializeable into XML files. For my example, I created an abstract class that I called BaseFilter, and 2 derived classes called AddFilter and MultiplyFilter. I wanted to use an example that would be easy to understand when I put this together. Here are my class definitions:
1: public abstract class BaseFilter
2: {
3: public abstract int NumberOperation(int number1, int number2);
4: }
5:
6: public class AddFilter : BaseFilter
7: {
8: public override int NumberOperation(int number1, int number2)
9: {
10: return number1 + number2;
11: }
12: }
13:
14: public class MultiplyFilter : BaseFilter
15: {
16: public override int NumberOperation(int number1, int number2)
17: {
18: return number1 * number2;
19: }
20: }
Now, I need to address the idea of serialization for these classes, so I marked the derived classes with the [Serializable()] Attribute, and I used XMLInclude in my abstract class to allow me to deserialize my objects. The final version of my classes looks like this:
1: [System.Xml.Serialization.XmlInclude(typeof(MultiplyFilter))]
2: [System.Xml.Serialization.XmlInclude(typeof(AddFilter))]
3: public abstract class BaseFilter
4: {
5: public abstract int NumberOperation(int number1, int number2);
6: }
7:
8: [Serializable()]
9: public class AddFilter : BaseFilter
10: {
11: public override int NumberOperation(int number1, int number2)
12: {
13: return number1 + number2;
14: }
15: }
16:
17: [Serializable()]
18: public class MultiplyFilter : BaseFilter
19: {
20: public override int NumberOperation(int number1, int number2)
21: {
22: return number1 * number2;
23: }
24: }
Now that I have my classes defined, I nned to think about how to implement my factory pattern here. My factory class will also control the deserialization of the XML files. My factory class ended up looking like this:
1: public class FilterFactory
2: {
3: private BaseFilter _filter;
4:
5: public FilterFactory()
6: {
7:
8: }
9:
10: public void ReadFile(string fileName)
11: {
12: if (!File.Exists(fileName))
13: throw new Exception("Error: File does not exist.");
14:
15: using (FileStream fStream = new FileStream(fileName, FileMode.Open))
16: {
17: XmlSerializer serializer;
18: serializer = new XmlSerializer(typeof(BaseFilter));
19: _filter = (BaseFilter)serializer.Deserialize(fStream);
20: }
21: }
22:
23: public int NumberOperation(int number1, int number2)
24: {
25: return _filter.NumberOperation(number1, number2);
26: }
27: }
To put it all together, I wrote a small console application that tested all this.
1: class Program
2: {
3: static void Main(string[] args)
4: {
5: // Create a sample serialized multiply filter.
6: using (FileStream fStream =
7: new FileStream("multiply.xml", FileMode.Create))
8: {
9: MultiplyFilter mFilter = new MultiplyFilter();
10: XmlSerializer serializer = new XmlSerializer(typeof(BaseFilter));
11: serializer.Serialize(fStream, mFilter);
12: }
13:
14: // Create a sample serialized add filter.
15: using (FileStream fStream =
16: new FileStream("add.xml", FileMode.Create))
17: {
18: AddFilter aFilter = new AddFilter();
19: XmlSerializer serializer = new XmlSerializer(typeof(BaseFilter));
20: serializer.Serialize(fStream, aFilter);
21: }
22:
23: // Get an instance to my factory.
24: FilterFactory factory = new FilterFactory();
25:
26: factory.ReadFile("multiply.xml");
27: Console.WriteLine("Answer for 5 and 2 is: {0}",
28: factory.NumberOperation(5, 2));
29:
30: Console.WriteLine(System.Environment.NewLine);
31:
32: factory.ReadFile("add.xml");
33: Console.WriteLine("Answer for 5 and 2 is: {0}",
34: factory.NumberOperation(5, 2));
35:
36: Console.WriteLine(System.Environment.NewLine);
37: Console.WriteLine("Press any key to continue.");
38: Console.ReadKey();
39: }
40: }
In the console application, the first thing I did was created a sample filter for both of my supported operations and then serialzed them. This doesn’t necessarily make sense in a real world example, but it works for the sake of this example. Then I created a factory instance, which allowed me to create instances of either type I wanted at run time. To do this, my factory class reads the XML file, and deserializes it, which all happens in the ReadFile function. To prove that I had the correct derived instances, I called the NumberOperation method. The factory created an instance of the correct derived class in each case based on the results of the deserialization. The output of this console application looked like this:
That works as advertised, but there was one thing about this solution that I didn’t care for. Astute readers will notice that I had to use the XMLInclude directive on my abstract class. This basically says that when a BaseFilter is deserialized, it could actually be a MultiplyFilter or an AddFilter. This means that if I added a SubtractFilter I would have to modify the base class to account for the new type so that it could be deserialized. What happens if the abstract definition was in an assembly that I didn’t have the source for? I’ll have to put some brain cells on that one when I get some time and see if I can come up with a solution.
December 4th, 2007 at 2:41 pm
In my C++ example, I push the deserialization responsibility to the concrete, child classes. Then I have a map from the “name” of the class to a pointer for the deserialization function. That way I can have filters that have all kinds of extra configuration parameters (think a polynomial and coefficients for your example). Fundamentally, I think that is how the responsibility must be divided. Being ignorant of dotNET, I am guessing that would involve delegates and may complicate your serialization logic. Of course, when I tried implement more of my C++ program, my program inexplicably crashes when trying to do any XML parsing. I am sure this is due to a screwed-up library installation — but I guess that’s not a problem with the dotNET answer.
December 5th, 2007 at 3:33 am
This is really interesting to me. It took me a bit to (generally) understand the code, but it does seem like an elegant solution.
March 23rd, 2008 at 9:38 am
Hi,
Many thanks for this, you’ve saved me hours, if not days of head scratching. I’ve been avidly learning about GoF Design Patterns of late, and since much of my work involves web services and serialization.
Your well explained example will allow me to apply the factory pattern into my C#/AMF3 developments.
Many thanks.
March 24th, 2010 at 6:25 am
Thanks for publishing this blog. Your writing technique is very clear. I have a query for you as I see an issue with your deserialisation methodology. Concrete classes in nearly all cases will have specific implementations e.g. a “DivideFilter” might have GetNumerator() and GetDenomitor() methods. These methods will be specific/to that filter alone and *not* relevant to the other concrete filters. This brings me to my question: how do we adjust the above so that we can deserialise data that is specific to a concrete class?
March 26th, 2010 at 10:48 am
Finbar,
I dug out my old example and added a divide filter to it. A few ideas for you…
1. I added a method to make sure that the division was valid right in the class and that works as is. (in this case an exception is thrown for invalid division)
[Serializable]
public class DivideFilter : BaseFilter
{
public override int NumberOperation(int number1, int number2)
{
if (isValidDivision(number1, number2))
{
return number1/number2;
}
throw new Exception(“Error: Division by Zero error!”);
}
public bool isValidDivision(int number1, int number2)
{
return (number2 != 0);
}
public void Foo()
{
Console.WriteLine(“This method is specific to the divide filter.”);
}
}
2. If you really need to have access to something in your base class it gets a little trickier. I created a Foo method off my DivideFilter class above that is unique to that class. I then exposed the filter from inside the FilterFactory class, then in my console application I just do a soft cast to trap the case where I have a DivideFilter, then I can call the method. By doing a soft cast, no exception is thrown for other types of filters.
// Create a sample divide filter.
using (var fStream = new FileStream(“divide.xml”, FileMode.Create))
{
var dFilter = new DivideFilter();
var serializer = new XmlSerializer(typeof(BaseFilter));
serializer.Serialize(fStream, dFilter);
}
factory.ReadFile(“divide.xml”);
Console.WriteLine(“Answer for 5 and 2 is {0} [{1}]“, factory.NumberOperation(5, 2), factory.Type);
filter = factory.Filter as DivideFilter;
if (filter != null)
{
// This method is unique to the divide filter.
filter.Foo();
}
Hope that helps!
Jack
July 17th, 2010 at 5:08 pm
Thanks for that Jack.
I was thinking that perhaps it would be possible(it would be nice to avoid casting, if possible of course) to use an interface to accommodate functionality that is not specific to the base class. The interface, lets say IFilterSpecifics could be exposed by the base and all derived classes and thus the cast could be somehow avoided.
July 22nd, 2010 at 8:18 am
@Finbar,
Let me know if you come up with something, that sounds like it could be a nice solution.