- ASP.NET 3.5 Social Networking
- Andrew Siemer
- 842字
- 2025-02-26 05:48:00
Factory pattern using StructureMap
We discussed the factory pattern (under the Factories section) to help us in our efforts for loosely coupled objects of our application. We have also said that we plan to use a Test- Driven Development process. In order to achieve both of these easily, we will be using a framework called StructureMap. Get more information on http://structuremap.sourceforge.net/Default.htm. StructureMap is a dependency injection framework. Its primary goal is to help us to reduce the mechanical costs of good object-oriented design. It also helps us to have a more flexible application for testing purposes.
StructureMap is very easy to use. I have mostly covered how to get it set up in Appendix A at the end of the book. But if you don't want to read that now, I will give you a down and dirty here. Once you have downloaded the StructureMap files, add them to your bin
directory. Then add a reference to the StructureMap in your projects. Add an XML file to your solution and share it amongst your projects calling it StructureMap.config
. In that file, enter the following:
<?xml version="1.0" encoding="utf-8" ?> <StructureMap> <Assembly Name="Fisharoo.FisharooWeb" /> <Assembly Name="Fisharoo.FisharooCore" /> </StructureMap>
Now for every class that you create, add an attribute just above the class that looks something like this:
[Pluggable("Default")]
Once you have defined your class, you can define the interface that the class inherits from, open your interface, and add the following attribute.
[PluginFamily("Default")]
Once you have this in place, you can then write something along the following lines when instantiating a class:
IPerson person = ObjectFactory.GetInstance<IPerson>();
Note
Using interfaces like this is a good design regardless of other technologies such as StructureMap. It really helps to create a loosely coupled environment. Having said that, StructureMap relies heavily on the use of interfaces to define a PluginFamily. All members of the same PluginFamily are located through the process of reflection and grouped by the interfaces that they inherit from (example: person
and PersonStub
would both inherit IPerson
and would therefore be part of the same PluginFamily). So in the interest of saving some space in this book, you can safely assume that for every class that has a pluggable attribute, you will need to create a simple interface that defines the default class. I will point out the interface usage, but will not discuss them in detail.
This instantiation says a lot. We no longer have a direct coupling between the person
object and the client code using the person
object. Instead, we have told StructureMap to go and load the default instance of a person
object whatever that may be at the time we asked for it.
This decoupling is very nice. But once we have StructureMap woven into our application and we start writing our tests, we can now also tell StuctureMap which version of an object we would like to use at a specified time. For testing purposes, it is not always best to use our production objects. Perhaps the person
object requires too many resources, or perhaps it relies on several other objects to perform its tasks. In that case, we may want to create a PersonStub
class. This would be a lightweight class. It would of course conform to the IPerson
interface in the same way that the production person does. The only major difference as far as StructureMap is concerned is that the attribute at the top of the class might look something like this:
[Pluggable("Stubbed")]
We have two ways to let StructureMap know that we want to use the Stubbed
person rather than the Default
person. We can programmatically specify it say while running tests. Or in case we have stubbed out a class for our development environment, to use something closely represents what we would have access to in production, we can specify what to use in the StructureMap.config
file.
In the case of programmatic control, we can simply use the methods that are provided by the ObjectFactory
class. We can use the InjectStub
method to override the class to be used the next time it is called.
ObjectFactory.InjectStub(typeof (IPerson), new PersonStub());
The next time an IPerson
is loaded by the ObjectFactory
class, StructureMap will use the PersonStub
class instead of the standard Person
class.
That's great! But how do I revert StructureMap back to using the Person
class once my testing is completed?
ObjectFactory.ResetDefaults();
This works well for testing purposes. But what about swapping something out in a more permanent way? In order to specify which class to use in the configuration file, you would enter something as shown in the following code snippet. Note that the DefaultKey
has the Stubbed
description.
<PluginFamily Assembly="Fisharoo.FisharooCore" Type="Fisharoo.FisharooCore.Core.Impl.Person" DefaultKey="Stubbed" /> <!-- Default, Stubbed -->
Note that at the bottom, I have added a comment that defines the possible classes that can be specified.
The default key name doesn't have to be the word "Default", something that you are entirely in control of. You could use a different default tag for each class. We could have just as easily used PersonDefault
or just Person
. It is all in how you set it up. The interface's PluginFamily
attribute defines the default key.