Design patterns Factory Simple example of Factory that uses an IoC (C#)


Example

Factories can be used in conjunction with Inversion of Control (IoC) libraries too.

  • The typical use case for such a factory is when we want to create an object based on parameters that are not known until run-time (such as the current User).
  • In these cases it can be sometimes be difficult (if not impossible) to configure the IoC library alone to handle this kind of runtime contextual information, so we can wrap it in a factory.

Example

  • Suppose we have a User class, whose characteristics (ID, Security clearance level, etc.), are unknown until runtime (since the current user could be anyone who uses the application).
  • We need to take the current User and obtain an ISecurityToken for them, which can then be used to check if the user is allowed to perform certain actions or not.
  • The implementation of ISecurityToken will vary depending on the level of the User - in other words, ISecurityToken uses polymorphism.

In this case, we have two implementations, which also use Marker Interfaces to make it easier to identify them to the IoC library; the IoC library in this case is just made up and identified by the abstraction IContainer.

Note also that many modern IoC factories have native capabilities or plugins that allow auto-creation of factories as well as avoiding the need for marker interfaces as shown below; however since not all do, this example caters to a simple, lowest common functionality concept.

//describes the ability to allow or deny an action based on PerformAction.SecurityLevel
public interface ISecurityToken
{
    public bool IsAllowedTo(PerformAction action);
}

//Marker interface for Basic permissions
public interface IBasicToken:ISecurityToken{};
//Marker interface for super permissions
public interface ISuperToken:ISecurityToken{};

//since IBasictoken inherits ISecurityToken, BasicToken can be treated as an ISecurityToken
public class BasicToken:IBasicToken
{
     public bool IsAllowedTo(PerformAction action)
     {
         //Basic users can only perform basic actions
         if(action.SecurityLevel!=SecurityLevel.Basic) return false;
         return true;
     }
}

public class SuperToken:ISuperToken
{
     public bool IsAllowedTo(PerformAction action)
     {
         //Super users can perform all actions         
         return true;
     }
}

Next we will create a SecurityToken factory, which will take as a dependency our IContainer

public class SecurityTokenFactory
{
   readonly IContainer _container;
   public SecurityTokenFactory(IContainer container)
   {
      if(container==null) throw new ArgumentNullException("container");
   }

   public ISecurityToken GetToken(User user)
   {
      if (user==null) throw new ArgumentNullException("user);
      //depending on the user security level, we return a different type; however all types implement ISecurityToken so the factory can produce them.
      switch user.SecurityLevel
      {
          case Basic:
           return _container.GetInstance<BasicSecurityToken>();
          case SuperUser:
           return _container.GetInstance<SuperUserToken>();
      }
   }
}

Once we've registered these with the IContainer:

IContainer.For<SecurityTokenFactory>().Use<SecurityTokenFactory>().Singleton(); //we only need a single instance per app
IContainer.For<IBasicToken>().Use<BasicToken>().PerRequest(); //we need an instance per-request
IContainer.For<ISuperToken>().Use<SuperToken>().PerRequest();//we need an instance per-request 

the consuming code can use it to get the correct token at runtime:

readonly SecurityTokenFactory _tokenFactory;
...
...
public void LogIn(User user)
{
    var token = _tokenFactory.GetToken(user);
    user.SetSecurityToken(token);
}

In this way we benefit from the encapsulation provided by the factory and also from the lifecycle management provided by the IoC library.