Geeks With Blogs
Blake Caraway

I've received a handful of comments/questions surrounding part of the code sample from my previous post.

The comments I received have to do with the few lines of code in the Authentication WebService that included a custom interface and a call to a third party tool. Here's the code in question:

public class Authentication : WebService
{
private IWebContext _webContext = ObjectFactory.GetInstance<IWebContext>();

[WebMethod]
public bool IsLoggedIn()
{
return this._webContext.IsUserAuthenticated;
}
}

 

Create An Abstraction

IWebContext is an interface I created for use within my web site that abstracts calls to lower level ASP.Net stuff like QueryString, Session, and Identity thru the HTTPContext object. Often I want to have a testable object in an ASP.Net web project that will depend on something at the ASP.Net lower level. The way to put a seam in my code that decouples the object I want to test from the ASP.Net stuff is to abstract it away...into something like IWebContext. Here's the IWebContext interface definition:

public interface IWebContext
{
bool IsUserAuthenticated { get; }
void Clear();
void Save(string key, object value);
object Get(string key);
string GetQuerystringValue(string key);
void Set(string s, object value);
}
 

Dependency Inversion / Inversion of Control

Regarding the ObjectFactory.GetInstance<IWebContext>(); line of code -- to help with decoupling, I use the Dependency Inversion Principle (DIP). Another name for DIP is Inversion of Control (IoC). The DIP has been proposed by Robert C. Martin. It states that:

High level modules should not depend upon low level modules. Both should depend upon abstractions. Abstractions should not depend upon details. Details should depend upon abstractions.

In short, strive to make your classes depend upon abstractions, not concretions. When you see the keyword 'new' - that's a tight coupling to a specific concrete type. If you cannot easily isolate an object from it's dependencies, and if this makes it more difficult to test, then that's a tight coupling that needs to be addressed. You need to invert the dependencies.

 

The Flavors of DIP - Inverting with StructureMap

There are two main flavors of Dependency Inversion - Dependency Injection and Service Location. Beyond that, Dependency Injection has two main forms - Constructor Injection and Setter Injection. Constructor Injection is when an object is instantiated with a dependency. Setter Injection is when a dependency is provided through some sort of setting method/property.

The other main form of Dependency Injection is Service Location. Using a method like StructureMap's ObjectFactory.GetInstance() is another way to locate a service/dependency. In the code sample, the ObjectFactory object lives in a third party tool called StructureMap ( http://structuremap.sourceforge.net ). We ask StructureMap.ObjectFactory to return an instance of a certain type (i.e. IWebContext). This effectively inverts the control of resolving dependencies from the object needing to use the dependency to a third party, adhering to the DIP, and promoting looser object coupling.

If you are interested in learning more about the open source project StructureMap, visit the project page on SourceForge at http://structuremap.sourceforge.net/. I won't try and detail all of the implementation details of StructureMap, but here's a quick rundown of the basic usage.

StructureMap allows us to decorate abstractions with a [PluginFamily] attribute, as well as a [Pluggable] attribute on the types that implement the abstraction. When a call is made to ObjectFactory.GetInstance<IWebContext>(), StructureMap finds the classes that implement the type being requested and resolves, instantiates, and returns the correct concrete instance. Let's look at an example:

Interface: This is the IAccountRepository interface, decorated with the PluginFamily attribute and key name of 'Default'.

   1:  [PluginFamily("Default")]
   2:  public interface IAccountRepository
   3:  {
   4:      BillToAccount[] GetBillingAccountsForUser(User user);
   5:      ShipToAccount GetShipToAccount(AccountIdentifier accountIdentifier);
   6:      BillToAccount GetBillToAccount(AccountIdentifier accountIdentifier);
   7:  }

 

Default Implementation: Here we have an implementation of the IAccountRepository interface decorated with the Pluggable attribute and a key name of 'Default'.

   1:  [Pluggable("Default")]
   2:  public class AccountRepository : AccountRepositoryBase
   3:  {
   4:      ...
   5:  }

 

Cached Implementation: Here we have another implementation of the IAccountRepository interface decorated with the Pluggable attribute and a key name of 'Cached'.

   1:  [Pluggable("Cached")]
   2:  public class CachedAccountRepository : IAccountRepository
   3:  {
   4:      ...
   5:  }
 

When code needs to make use of a dependency like IAccountRepository, it can ask ObjectFactory for the default instance in this way:

_accountRepository = ObjectFactory.GetInstance<IAccountRepository>();

You can have StructureMap return specific implementations of IAccountRepository in a couple easy ways: you can set the PluginFamily attribute's key on the interface to match that of the desired "default" Pluggable instance like this:

   1:  [PluginFamily("Cached")]
   2:  public interface IAccountRepository
   3:  {
   4:      ...
   5:  }

 

...Or you may ask for a particular instance by name, like this:

_cachedAccountRepository = ObjectFactory.GetNamedInstance<IAccountRepository>("Cached");

 

Dynamic Behavior / Configuration

StructureMap also allows you to specify type configurations (and all sorts of other behaviors your system might need) in a config file. Some may prefer the config file method because it's a little more obvious (in a file, rather than living inside the code at various locations) as to what objects implement certain interfaces and what the default behavior is for a particular abstraction. Having these settings in a config file can also be helpful when you have an automated build that might require different system behavior (i.e. different implementation of various abstractions) based on the particular environment you are deploying to or maybe the state of an entire subsystem.

When deploying your system, behavior can be switched around by having your build tool like NAnt issue an 'XMLPoke' command to change key names inside the StructureMap.config file. Also, your application may need to behave differently based on some runtime situations like database or service availability (think 'offline mode'). This can easily be achieved by using a tool like StructureMap. Simply request a different implementation of a type using the 'ObjectFactory.GetNamedInstance' method as shown above and your system can gracefully adapt to any unexpected environmental situation.

 

Hopefully I've shown here the importance of abstracting away the factors that can make code more difficult to get under test, as well as a couple mechanics on how to approach the problem.

 

/bc

 

 

Posted on Sunday, August 12, 2007 11:30 AM Design Patterns | Back to top


Comments on this post: Decouple Your Code For Testability

# re: Decouple Your Code For Testability
Requesting Gravatar...
Very, very intersting post here and I am going to begin using StructureMap (or something similar) immediately.

Very helpful and actually vital information on writing dynamic and more easily tested code.
Left by Jake Weakley on Aug 13, 2007 9:50 AM

Comments have been closed on this topic.
Copyright © Blake Caraway | Powered by: GeeksWithBlogs.net