C# Corner

Abstract and Delegate-based Factories in .NET

In part 2 of a series of columns on using factory patterns, Patrick Steele shows how factory patterns can be used to make your applications more flexible.

In part 1 of this series, I began exploring software factory patterns and how they can be used to enable more flexible application code. In this installment, I'll look at abstract factories and delegate-based factories and how they can be used to support customizations.

The Abstract Factory
Many times, an application that uses a factory will provide a "default" implementation of that factory. This way, you have a complete working factory that creates all of the different components you need. If one or two of those components have to be customized, you derive from the base factory and implement the customizations.

Let's revisit our application from earlier. Instead of using an interface, we'll use a base class for our factory. Also, our application's requirements have expanded and we now do some logging. The logs are stored alongside our data source (either File, FTP or in Azure), so we've pushed creation of our logger into the factory as well:

	class ProcessorFactory
	{
		public virtual ITextProcessor CreateProcessor()
		{
			return new FileProcessor();
		}

		public virtual ILogger CreateLogger()
		{
			return new FileLogger();
		}
	}

Notice that the methods are virtual. This allows customization of only certain parts of the factory. Suppose the client wants logging messages to always go to a database? We implement an ILogger that writes to a database:

	class DBLogger : ILogger
	{
		public void Log(string message)
		{
			// write message to database
		}
	}

Then create a new factory to handle the creation of the specialized logger:

	class CustomizedProcessorFactory : ProcessorFactory
	{
		public override ILogger CreateLogger()
		{
			return new DBLogger();
		}
	}

This solves the ripple-down effect we saw with interfaces. If a new component is needed and a default implementation is done in the base ProcessorFactory, all derived classes will automatically get that default implementation via inheritance. If the derived classes need customization, they override the CreateXXX method.

Customized Object Creation
The Factory Pattern is usually associated with a group of patterns called "Creational," since it deals with the creation of objects. By default, most objects are created with the "new" keyword. However, as we saw in a previous article (Inversion of Control Patterns for the Microsoft .NET Framework, August 2010), we sometimes want to change how an object gets created. A pluggable factory is one way to accomplish this.

In Microsoft's ASP.NET MVC (Model-View-Controller) framework, a "controller" (which is a C# class) handles a request that comes in from the Web server. Different controllers handle different URL's. Once the MVC framework has decided the type of controller to handle a request, it uses a factory to instantiate the controller. A default controller factory is provided in the MVC framework and looks something like this:

	class DefaultControllerFactory
	{
		public virtual IController
GetControllerInstance(Type controllerType)
		{
			return
(Controller)Activator.CreateInstance(controllerType);
		}
	}

While this is highly simplified, you can see that the creation of the controllers is done by the factory, not the MVC framework itself. This lets us easily change the way our controllers get created. Suppose you want to use the Castle Windsor IoC container to manage your controllers and their dependencies? In that case, using Activator.CreateInstance wouldn't work because you want your IoC container to handle the instantiation of the controllers.

Instead, we can subclass the DefaultControllerFactory and override the method that creates the controller -- and we can use our IoC container to handle the creation! A simple example might look something like this:

	class WindsorController : DefaultControllerFactory
	{
		private readonly IWindsorContainer container;

		public WindsorController(IWindsorContainer container)
		{
			this.container = container;
		}

		public override Controller CreateController(Type controllerType)
		{
			return container.Resolve(controllerType) as Controller;
		}

		public override void ReleaseController(Controller controller)
		{
			container.Release(controller);
		}
	}

Again, this is simplified and is for illustration purposes only. Note that we also did an override of the ReleaseController method from the default factory. This gives us an opportunity to let our IoC container know that we're done with the controller. Now we just tell the MVC framework to use this factory instead of the default one and our controllers now get resolved via our IoC container!

Delegate-Based Factories
Some developers have adopted a unique approach to factories. Instead of using a base class or interfaces, they use delegates. After all, a delegate represents a method with a particular signature. Instead of having to build separate factory classes that do little more than use the "new" keyword to create an object, delegates can be used to represent the actual factory method.

Let's see how we might use delegates to define our ProcessorFactory we introduced earlier:

	public delegate ITextProcessor CreateProcessorDelegate();
	public delegate ILogger CreateLoggerDelegate();

	class DelegateProcessorFactory
	{
		public CreateProcessorDelegate CreateProcessor { get; set; }
		public CreateLoggerDelegate CreateLogger { get; set; }

		public DelegateProcessorFactory()
		{
			this.CreateProcessor = new CreateProcessorDelegate(
delegate { return new FileProcessor(); });
			this.CreateLogger = new CreateLoggerDelegate(
delegate { return new FileLogger(); });
		}
	}

As before, this provides a default implementation that does processing and logging to files. This may seem like a bit of work to define all of those delegates. And actually, it is -- especially as the number of factory methods grows. If you use .NET Framework 3.5, lambdas can make this code much easier, as well as reduce the need to create your own delegates thanks to the framework's built-in Func:

	class LambdaProcessorFactory
	{
		public Func<ITextProcessor> CreateProcessor { get; set; }
		public Func<ILogger> CreateLogger { get; set; }

		public LambdaProcessorFactory()
		{
			this.CreateProcessor = () => new FileProcessor();
			this.CreateLogger = () => new FileLogger();
		}
	}

Customizing this factory is easy and doesn't require any subclassing. Simply assign a new lambda expression to be used in the creation of the processor and logger. Earlier, we had to create a specialized factory to use the DBLogger. With the lambda approach, this customization is much easier:

		var factory = new LambdaProcessorFactory();
		factory.CreateLogger = () => new DBLogger();

Conclusion
As you can see, the factory pattern can be a powerful way to allow your applications to be more flexible and easier to adapt to new conditions in the future. It's more common in libraries and API's, but careful use inside your own applications will lead to big benefits down the road.

About the Author

Patrick Steele is a senior .NET developer with Billhighway in Troy, Mich. A recognized expert on the Microsoft .NET Framework, he’s a former Microsoft MVP award winner and a presenter at conferences and user group meetings.

comments powered by Disqus

Featured

  • Creating Reactive Applications in .NET

    In modern applications, data is being retrieved in asynchronous, real-time streams, as traditional pull requests where the clients asks for data from the server are becoming a thing of the past.

  • AI for GitHub Collaboration? Maybe Not So Much

    No doubt GitHub Copilot has been a boon for developers, but AI might not be the best tool for collaboration, according to developers weighing in on a recent social media post from the GitHub team.

  • Visual Studio 2022 Getting VS Code 'Command Palette' Equivalent

    As any Visual Studio Code user knows, the editor's command palette is a powerful tool for getting things done quickly, without having to navigate through menus and dialogs. Now, we learn how an equivalent is coming for Microsoft's flagship Visual Studio IDE, invoked by the same familiar Ctrl+Shift+P keyboard shortcut.

  • .NET 9 Preview 3: 'I've Been Waiting 9 Years for This API!'

    Microsoft's third preview of .NET 9 sees a lot of minor tweaks and fixes with no earth-shaking new functionality, but little things can be important to individual developers.

  • Data Anomaly Detection Using a Neural Autoencoder with C#

    Dr. James McCaffrey of Microsoft Research tackles the process of examining a set of source data to find data items that are different in some way from the majority of the source items.

Subscribe on YouTube