The factory pattern is responsible for performing the direct
instantiation of an object. It can take some sort of designator: a string,
a generic type, an enumeration, or some other key to dynamically create the
reference of the object for you. Let us take a look at an object.
Listing 4
public class Verse
{
public string Name { .. }
public int Number { .. }
public string Text { .. }
internal Verse(string name, int number, string text) { .. }
}
Note that the class Verse has an internal constructor, so
other classes within the Visual Studio project can instantiate it, but the
general public that consumes this project cannot. This forces any
activity on this object to go through the factory. The following is the
factory definition.
Listing 5
public static class verseFactory
{
public static Verse GetVerse(string name)
{
//Access the data layer to get the specific verse by name
}
public static VerseCollection GetVerses(object parent)
{
//Access the data layer to create a collection of verses by name
}
}
The factory is a static definition; it handles performing data retrieval from
the data layer, as well as the creation of the verse objects. The method
definition for GetVerses could look something like this:
Listing 6
VersesDataGateway gateway = new VersesDataGateway();
DataTable versesTable = gateway.GetVerses();
VerseCollection versesList = new VerseCollection();
foreach (DataRow versesRow in versesTable.Rows
{
Verse verse = new Verse(
versesRow["Name"].ToString(),
(int)versesRow["Number"],
versesRow["Text"].ToString());
versesList.Add(verse);
}
return versesList;
Notice that the factory is handling the instantiating of the
object. Because this class would contain several methods that retrieve
data from the back-end data store, I would move the verse creation capabilities
into another method, but you get the idea. This is what the factory could
do to get the data from the data store. The factory can handle more
responsibility, such as updating the changes from the Verse class back to the
data store, deleting items from the data store or any other responsibility you
may want to give it.
However, suppose you wanted to give the factory a more dynamic
functionality. For instance, suppose you wanted to have a collection of
objects that could be instantiated dynamically, based on a key value. This
factory could then add/remove support to keys very easily, but how would that
be implemented? To illustrate, let us use an example. Suppose you had an
evaluator factory which returned the appropriate evaluator based on a string
key that was passed in. This list of evaluators can be stored in the
configuration file.
Listing 7
<evaluatorSettings>
<evaluators>
<add key="month" type="Nucleo.Evaluators.MonthEvaluator,Nucleo" />
<add key="day" type="Nucleo.Evaluators.DayEvaluator,Nucleo" />
<add key="year" type="Nucleo.Evaluators.YearEvaluator,Nucleo" />
<add key="week" type="Nucleo.Evaluators.WeekEvaluator,Nucleo" />
</evaluators>
</evaluatorSettings>
If you are interested in creating a custom configuration
section and their child collections, you can read my series of articles from
the links below.
·
http://dotnetslackers.com/articles/customconfiguration/Custom_Configuration_Sections.aspx
·
http://dotnetslackers.com/articles/customconfiguration/Configuration_Section_Validators.aspx
·
http://dotnetslackers.com/articles/customconfiguration/Custom_Configuration_Collections.aspx
·
http://dotnetslackers.com/articles/customconfiguration/Custom_Provider_Configuration_Sections.aspx
Our factory would be able to connect to the custom
configuration section and extract the object keys/types that it needs to
instantiate. Below is a possible implementation of that solution. Note that
the evaluators' collection would most likely be a static collection of
evaluators, which the consumer of the factory can reference via the key. By
giving each of the evaluators a common base class, each custom evaluator can be
easily referenced.
Listing 8
foreach (EvaluatorElement element in evaluatorSection.Evaluators)
{
Type evalType = Type.GetType(element.Type);
EvaluatorBase evalBase = (EvaluatorBase)Activator.CreateInstance(evalType);
_evaluators.Add(element.Key, evalBase);
}