Exploring the Factory Design Pattern
Doug Purdy
Microsoft Corporation
February 2002
Summary: Discusses the Factory creational pattern that uses a specialized object solely to create other objects, much like a real-world factory. The logical and physical models of this pattern are examined, as is one use of this pattern in the Microsoft .NET Framework. (11 printed pages)
Contents
Introduction
Factory Pattern
Logical Model
Physical Model
Factory Pattern in the .NET Framework
Conclusion
Introduction
Software changes. This is a rather obvious statement, but it is a fact that must be ever present in the minds of developers and architects. Although we tend to think of software development as chiefly an engineering exercise, the analogy breaks down very quickly. When was the last time someone asked the designers of the Empire State building to add ten new floors at the bottom, put a pool on the top, and have all of this done before Monday morning?
This is a daunting challenge. We must design systems that are capable of changing at a moment's notice. Be it requirements, technology, or personnel, there will be change and we must be prepared. If we forget this, we do so at our own peril.
Although this can be a frightening proposition, there are a great many tools at our disposal to ensure that systems are designed to change rapidly while reducing the negative impact these alterations can bring. One such tool is design patterns. Design patterns represent a wealth of collective knowledge and experience. The proper use of these patterns will help to ensure that systems are malleable, enabling rapid change.
This article continues our discussion of these design patterns, which began with Exploring the Observer Design Pattern. This series focuses on the examination of various design patterns and their use in the Microsoft® .NET Framework. Over the course of this article, we will examine one of the most commonly used patterns, the Factory pattern.
Factory Pattern
An important facet of system design is the manner in which objects are created. Although far more time is often spent considering the object model and instance interaction, if this simple design aspect is ignored it will adversely impact the entire system. Thus, it is not only important what an object does or what it models, but also in what manner it was created.
Since most object-oriented languages and runtimes provide object instantiation (e.g. new, newobj, etc.) and initialization (e.g. constructors) mechanisms, there may be a tendency to simply use these facilities directly without forethought to future consequences. The overuse of this functionality often introduces a great deal of the inflexibility in the system, as the direct use of a language/run-time object instantiation function creates an explicit association between the creator and created classes. While associations are a necessary type of relationship in an object-oriented system, the coupling introduced between classes is extremely difficult to overcome should requirements change (as they always do).
Since the need to reduce the inherent coupling associated with instance creation spans systems, it was not uncommon to see the same types of solutions being used in a variety of different applications and frameworks. Formalized within Design Patterns: Elements of Reusable Object-Oriented Software (Addison-Wesley Publishing Company, 1995), these solutions are known as creational patterns. Creational patterns describe object-creation mechanisms that enable greater levels of reuse in evolving systems.
One of the most widely used creational patterns is the Factory. This pattern is aptly named, as it calls for the use of a specialized object solely to create other objects, much like a real-world factory. In the following sections, we will examine the logical and physical models of this pattern, as well as discuss one such use of this pattern in the .NET Framework.
Logical Model
As with other design patterns, there are countless variations of the Factory pattern, although most variants typically used the same set of primary actors, a client, a factory, and a product. The client is an object that requires an instance of another object (the product) for some purpose. Rather than creating the product instance directly, the client delegates this responsibility to the factory. Once invoked, the factory creates a new instance of the product, passing it back to the client. Put simply, the client uses the factory to create an instance of the product. Figure 1 shows this logical relationship between these elements of the pattern.
Figure 1. Factory pattern logical model
The factory completely abstracts the creation and initialization of the product from the client. This indirection enables the client to focus on its discrete role in the application without concerning itself with the details of how the product is created. Thus, as the product implementation changes over time, the client remains unchanged.
While this indirection is a tangible benefit, the most important aspect of this pattern is the fact that the client is abstracted from both the type of product and the type of factory used to create the product. Presuming that the product interface is invariant, this enables the factory to create any product type it deems appropriate. Furthermore, presuming that the factory interface is invariant, the entire factory along with the associated products it creates can be replaced in a wholesale fashion. Both of these radical modifications can occur without any changes to the client.
Consider an application that models the assembly of a variety of personal computers. The application contains a ComputerAssembler class that is responsible for the assembly of the computer, a Computer class that models the computer being built, and a ComputerFactory class that creates instances of the Computer class. In using the Factory pattern, the ComputerAssembler class delegates responsibility for creation of Computer instances to the ComputerFactory. This ensures that if the instantiation and/or initialization process changes (e.g. new constructor, use of an activator, custom pooling, etc.), the ComputerAssembler does not need to change at all. This is the benefit of abstracting the creation of an object from its use.
In addition, suppose that the business requirements changed and a new type of computer needs to be assembled. Rather than modifying the ComputerAssembler class directly, the ComputerFactory class can be modified to create instances of a new computer class (assuming that this new class has the same interface as Computer). Furthermore, it is also possible to address this requirement by creating a new factory class (that has the same interface as ComputerFactory) that creates instances of the new computer class (again, with the same interface as Computer). As before, nothing in the ComputerAssembler class needs to change, all the logic just continues to work as it had previously. This is the benefit of abstracting the types of the product and factory into invariant interfaces.
Physical Model
Most implementations of the Factory pattern use two abstract classes, Factory and Product, to provide the invariant interfaces discussed in the logical model. Although we could utilize pure interfaces for this purpose, the use of abstract classes enables us to place common instance behavior in the abstract class. In addition, abstract classes offer versioning benefits over pure interfaces (see Base Class Usage Guidelines in the .NET Framework SDK for more information).
As shown in Figure 2, the Client uses an instance of a concrete subclass of Factory (ConcreteFactory) to create an instance of a concrete Product subclass (ConcreteProduct). Since all variables, parameters, and method return values are typed to the Factory and Product abstract classes, the Client is unaware of the introduction of these subclasses. This enables the introduction of new Factory and Product subclasses as requirements warrant; nothing needs to change within the Client. It is worth noting that this pattern does not dictate the use of the specific names (i.e. client, factory, product) used within the physical model, rather the use of these names is only to provide clarity in terms of the role each type plays in the pattern.
Figure 2. Factory pattern physical model
Using our previously defined computer assembly application, let's examine a simple implementation of the Factory pattern coded in C# and in Visual Basic .NET.
Computer and Computer Factory Classes (C#)
abstract class Computer {
public abstract int Mhz { get; }
}//Computer
abstract class ComputerFactory {
public abstract Computer GetComputer();
}//ComputerFactory
Computer and Computer Factory Classes (Visual Basic .NET)
MustInherit Class Computer
Public MustOverride ReadOnly Property Mhz() As Integer
End Class
MustInherit Class ComputerFactory
Public MustOverride Function GetComputer() As Computer
End Class
The above code fragment outlines the Computer and ComputerFactory abstract classes. These types represent the invariant factory and product interfaces required by the physical model. The ConcreteComputer and ConcreteComputerFactory classes found below extend these abstract classes. The ConcreteComputer class overrides the Mhz property of the Computer class, returning the value of 500. The ConceteComputerFactory overrides the GetComputer method of the ComputerFactory class returning a new instance of ConcreteComputer.
Concrete Computer and Concrete Computer Factory (C#)
class ConcreteComputer:Computer {
int _mhz = 500;
public override int Mhz {
get { return _mhz; }
}//Mhz
}//ConcreteComputer
class ConcreteComputerFactory:ComputerFactory {
public override Computer GetComputer() {
return new ConcreteComputer();
}//GetComputer
}//ConcreteComputerFactory
Concrete Computer and Concrete Computer Factory (Visual Basic .NET)
Class ConcreteComputer
Inherits Computer
Private _mhz As Integer = 500
Public Overrides ReadOnly Property Mhz() As Integer
Get
Return _mhz
End Get
End Property
End Class
Class ConcreteComputerFactory
Inherits ComputerFactory
Public Overrides Function GetComputer() As Computer
Return New ConcreteComputer()
End Function
End Class
The ComputerAssembler class, found below, provides one method, AssembleComputer. This method accepts one parameter that must be an object whose class extends the ComputerFactory abstract****class. This method then uses this factory instance to create an instance of some class that extends the Computer abstract class. Although the ComputerAssembler is blissfully unaware of the actual subclass of Computer that it is using, it is still able to print its type (via type introspection) and determine the MHz of the computer. The MainClass class, also found below, is the entry point for the sample. It creates an instance of the ComputerAssembler and invokes the AssembleComputer method passing an instance of a class that extends ComputerFactory.
ComputerAssembler and MainClass (C#)
class ComputerAssembler {
public void AssembleComputer(ComputerFactory factory) {
Computer computer = factory.GetComputer();
Console.WriteLine("assembled a {0} running at {1} MHz",
computer.GetType().FullName, computer.Mhz);
}//AssembleComputer
}//ComputerAssembler
class MainClass {
static void Main(string[] args) {
ComputerFactory factory = new ConcreteComputerFactory();
new ComputerAssembler().AssembleComputer(factory);
}//Main
}//MainClass
ComputerAssembler and MainClass (Visual Basic .NET)
Class ComputerAssembler
Public Sub AssembleComputer(ByVal factory As ComputerFactory)
Dim computer As Computer = factory.GetComputer()
Console.WriteLine("assembled a {0} running at {1} MHz", _
computer.GetType().FullName, computer.Mhz)
End Sub
End Class
Class MainClass
Public Shared Sub Main()
Dim args() As String = Environment.GetCommandLineArgs()
Dim factory As ComputerFactory = _
New ConcreteComputerFactory()
Call (New ComputerAssembler()).AssembleComputer(factory)
End Sub
End Class
In order to illustrate the benefits of factory and product type abstraction, suppose that the application was required to assemble two different computer types, rather than only one. For the purposes of this example, we create an additional factory, BrandXFactory, and a related product class, BrandXComputer, as shown below.
BrandXComputer and BrandXFactory (C#)
class BrandXComputer:Computer {
int _mhz = 1500;
public override int Mhz {
get { return _mhz; }
}//Mhz
}//BrandXComputer
class BrandXFactory:ComputerFactory {
public override Computer GetComputer() {
return new BrandXComputer();
}//GetComputer
}//BrandXFactory
BrandXComputer and BrandXFactory (Visual Basic .NET)
Class BrandXComputer
Inherits Computer
Private _mhz As Integer = 1500
Public Overrides ReadOnly Property Mhz() As Integer
Get
Return _mhz
End Get
End Property
End Class
Class BrandXFactory
Inherits ComputerFactory
Public Overrides Function GetComputer() As Computer
Return New BrandXComputer()
End Function
End Class
With the new factory and product in place, we now need to modify the Main method of the MainClass to determine which factory to use. If the appropriate string is passed in on the command line, the BrandXFactory will be used; if there is no such command line argument, the standard ComputerFactory will be used. This modification is shown below.
MainClass (C#)
class MainClass {
static void Main(string[] args) {
ComputerFactory factory = null;
if(args.Length > 0 && args[0] == "BrandX")
factory = new BrandXFactory();
else
factory = new ConcreteComputerFactory();
new ComputerAssembler().AssembleComputer(factory);
}//Main
}//MainClass
MainClass (Visual Basic .NET)
Class MainClass
Public Shared Sub Main()
Dim args() As String = Environment.GetCommandLineArgs()
Dim factory As ComputerFactory = Nothing
If args.Length > 0 And args(1) = "BrandX" Then
factory = New BrandXFactory()
Else
factory = New ConcreteComputerFactory()
End If
Call (New ComputerAssembler()).AssembleComputer(factory)
End Sub
End Class
Please note that there is no modification or re-implementation of the ComputerAssembler class whatsoever. This class is completely abstracted from the classes of the instances it assembles, as well as the creation and initialization of the same.
Factory Pattern in the .NET Framework
The Factory pattern has a long history at Microsoft. Those of you familiar with the internal workings of COM will no doubt recall the use of the IClassFactory interface used to create instances of COM co-classes. While the programming landscape has changed a great deal since the inception of COM, the benefits of this pattern were not lost on the designers of the .NET Framework. As with many other design patterns, examples of the Factory pattern can be found throughout the Framework. For the purposes of this article, we will examine one of the most notable uses of this pattern, which is found in Microsoft ASP.NET.
A well-know feature of ASP.NET is the Web Forms code model, consisting of the Page framework and associated Web Controls. Web Forms, in conjunction with other pieces of the .NET Framework, provide a rich development model for Web applications. Less well known, although of equal import, is the HTTP Runtime. The classes within this runtime provide the underlying HTTP request/response infrastructure required to support Web applications. Web Forms provide a high level of abstraction over the HTTP Runtime, allowing developers to focus on domain-specific user interfaces and business logic.
When an ASP.NET page (*.aspx) is requested from a Web server, a variety of actions are performed. First and foremost, IIS must determine what ISAPI extension will be used to service the request. Based on a set of pre-configured file extension mappings stored in the metabase, IIS loads the appropriate ISAPI extension (aspnet_isapi.dll) and invokes the exported HttpExtensionProc function. Assuming the ASP.NET process model is enabled (the default), the request is forward to the ASP.NET worker process (aspnet_wp.exe). Acting as an unmanaged host for the common language runtime, this process prepares the managed environment via numerous steps that culminate with the forwarding of the HTTP request to an instance of the managed System.Web.Hosting.HttpRuntime class found in the System.Web.dll assembly.
Although this instance of HttpRuntime is ultimately responsible for processing the pending request, the logic required to do so is not embedded within this class. If the designers of ASP.NET had made such a decision, every time the Web Forms code and/or architecture changed, the HttpRuntime class would also need to be revisited as well. Rather, ASP.NET uses an extensible mechanism to determine what logic to invoke when processing a request.
When the HttpRuntime::ProcessRequest method is invoked, a number of classes are utilized to determine which handler should process the request. These handlers contain the actual logic required to process the request. Handlers are classes that implement the System.Web.IHttpHandler interface. How does the HttpRuntime::ProcessRequest method determine what handler to invoke? Not surprisingly, it****relies on the Factory pattern.
If you have ever had the opportunity to examine the machine.config file, which provides the majority of the configuration information for the .NET Framework, you may have noticed the <httphandler> section under the <system.web> element (this file is located in the CONFIG subdirectory of the .NET Framework directory—on my machine C:\WINNT\Microsoft.NET\Framework\v1.0.3705\CONFIG). This segment of this config file provides a mapping between HTTP verbs, file extensions, and classes that implement the System.Web.IHttpHandlerFactory
interface (classes that implement IHttpHandler interface can also be registered here, but that is not germane to our discussion). The following shows the default mapping for ASP.NET pages (*.aspx):
<httpHandlers>
<add verb="*" path="*.aspx" type="System.Web.UI.PageHandlerFactory"/>
</httpHandlers>
When a request for an ASP.NET page is presented to the HttpRuntime instance, this mapping is consulted to determine what class instance should be utilized as a factory to create the correct handler instance. As the System.Web.UI.PageHandlerFactory class****is registered to perform this role, a new instance is created and the IHttpHandlerFactory::GetHandler method is invoked. This method returns an instance of the System.Web.UI.Page class to which the HttpRuntime instance delegates the request by invoking the IHttpHandler::ProcessRequest
method. The following sequence diagram shown in Figure 3 highlights this process. Please note that a number of classes, in addition to method parameters, have been omitted for the sake of brevity.
Figure 3. ASP.NET request processing
Creating your own specialized factory and associated handler is a fairly trivial exercise. The first step is the creation of classes that implement the IHttpHandlerFactory and IHttpHandler interfaces, as shown below.
Custom HttpHandlerFactory and HttpHandler (C#)
public class HttpHandlerFactoryImpl:IHttpHandlerFactory {
IHttpHandler IHttpHandlerFactory.GetHandler(
HttpContext context, String requestType,
String url, String pathTranslated ) {
return new HttpHandlerImpl();
}//IHttpHandlerFactory.GetHandler
void IHttpHandlerFactory.ReleaseHandler(
IHttpHandler handler) { /*no-op*/ }
}//HttpHandlerFactoryImpl
public class HttpHandlerImpl:IHttpHandler {
void IHttpHandler.ProcessRequest(HttpContext context) {
context.Response.Write("sample handler invoked...");
}//ProcessRequest
bool IHttpHandler.IsReusable { get { return false; } }
}//HttpHandlerImpl
Custom HttpHandlerFactory and HttpHandler (Visual Basic .NET)
Public Class HttpHandlerFactoryImpl
Implements IHttpHandlerFactory
Public Function GetHandler(ByVal context As HttpContext, _
ByVal requestType As String, ByVal url As String, _
ByVal pathTranslated As String) As IHttpHandler Implements _
IHttpHandlerFactory.GetHandler
Return New HttpHandlerImpl()
End Function
Public Sub ReleaseHandler(ByVal handler As IHttpHandler) _
Implements IHttpHandlerFactory.ReleaseHandler
'no-op
End Sub
End Class
Public Class HttpHandlerImpl
Implements IHttpHandler
Public Sub ProcessRequest(ByVal context As HttpContext) _
Implements IHttpHandler.ProcessRequest
context.Response.Write("sample handler invoked...")
End Sub
Public ReadOnly Property IsReusable() As Boolean _
Implements IHttpHandler.IsReusable
Get
Return False
End Get
End Property
End Class
The second step is the creation of the mapping between the HTTP verb, file extension, and factory class. Although this mapping can be placed in the <httpHandlers> section of the machine.config file, the recommended approach is to simply add this section to the web.config file located in the virtual directory of the Web application. When HTTP Runtime searches for handlers it will look in both the machine.config and web.config files. The following configuration segment shows that all HTTP request types (POST, GET, etc.) targeting files with the .sample extension will be handled by instances created by the previously defined HttpHandlerFactoryImpl factory class.
<httpHandlers>
<add verb="*" path="*.sample"
type="HttpHandlerFactoryImpl,SampleHandler" />
</httpHandlers>
The final step is the registration of the .sample file extension with IIS. In order for the ASP.NET HTTP Runtime to be invoked when a given file is requested, the file's extension must be mapped to the ASP.NET ISAPI extension (asp_isapi.dll). This task can be accomplished via the MMC snap-in for IIS by adding a new application extension mapping in the Web application configuration (as shown in Figure 4).
Figure 4. Registering the .sample extension
Conclusion
As we conclude this examination of the of the Factory pattern, it should be apparent that this pattern offers considerable benefits. From the abstraction of instance creation to the use of immutable product and factory interfaces, this pattern provides a clear mechanism to design flexible system that are highly adaptable. The inclusion of this pattern in the .NET Framework is a clear testament to it usefulness. Regardless of the nature of your application, this pattern can help it weather the winds of change.
Retired Content |
---|
This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This page may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. |
Retired Content |
---|
This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This page may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. |