The Factory Design Pattern

The Factory Design Pattern

by Amit Goel
08/11/2003

Software architects generally think in terms of high-level abstractions rather than low-level programming details. Representing a system in terms of high-level abstractions promotes understanding of the system and reduces its perceived complexity. One such set of abstractions is software design patterns. They have been successfully applied in the past to simplify and solve recurring problems in software design.

Given the above, it comes as no surprise that many popular software libraries make extensive use of design patterns so that the APIs can be abstracted out at a high level for the end programmer. The .NET Framework Class Library (FCL) is no exception. Since the FCL builds on the strengths of Microsoft's experience with developing software libraries, one can assume that instances of design patterns found in the FCL are fairly appropriate usages of these patterns. This article describes some instances of a commonly occurring design pattern in the FCL: the Factory Method design pattern.

Related Reading

.NET Framework Essentials
By Thuan L. Thai, Hoang Lam

Table of Contents
Index
Sample Chapter

The Factory Method Pattern

A detailed description of the Factory Method design pattern can be found elsewhere, so I will not try to describe it here. Suffice it to say that this pattern consists of two class hierarchies, one of Products, and one of Creators. Each ConcreteCreator class creates instances of specific ConcreteProduct classes using a factory method.

Factory methods are sometimes a more flexible way to instantiate a class than directly calling the constructor of the class, for the following reasons:

  • Unlike constructors, factory methods are not required to create a new object each time they are invoked. Factory methods can encapsulate the logic required to return a singleton instance of the class requested, or they can return an instance of the requested class from a pool of instances.
  • Factory methods can return any subtype of the type requested, and can require that clients refer to the returned object by its interface, rather than the implementation class. This enables an API to return objects without making their classes public.
  • The class for the object returned by a factory method need not even exist at the time the factory method is written. This is one of the classic benefits of polymorphism: "old code uses new code," which means that new classes can be added, and their instances returned by the factory method, without changing any of the existing code.

The FCL uses factory methods to its advantage in a number of places. Some of them are discussed below.

System.Collections.IEnumerable

The Iterator interface traverses a collection of objects. The interface aims to create a way to access the elements in a collection without exposing the internal structure of the collection. One way to implement this interface would be to have every collection class return an iterator object supporting a specific interface for iterating over collections of that particular type only. However, using this approach, one usually ends up exposing the internal structure of the collection being traversed. The FCL uses a better approach: it uses a factory method to return a polymorphic instance of the appropriate iterator class. All iterators returned by this factory method support a common interface for enumerating over all collection classes without exposing the internal structure of the collection being traversed. This has the added benefit of allowing changes to be made to the collection without requiring a change in the client code.

By specifying a GetEnumerator factory method in the IEnumerable interface and by having all collection classes implement this interface, the framework exposes one interface for obtaining an enumerator for a collection. ArrayList is one such class that implements the IEnumerable interface. ArrayList.GetEnumerator returns an IEnumerator object capable of enumerating over the elements in the ArrayList. Figure 1 shows the class hierarchy where the Iterator interface uses the Factory Method pattern.

Figure 1. Factory Method pattern for obtaining an enumerator for an ArrayList object

Since an iterator must have knowledge of the internal structure of the class over which it iterates, it is best to let every class provide its own iterator. IEnumerable classes provide their own iterators using GetEnumerator. In Figure 1, IEnumerable (the Creator) defines the interface by which an IEnumerator (the Product) is returned to the client. A ConcreteCreator (ArrayList) can then be called to return a ConcreteProduct (ArrayListEnumeratorSimple, a private inner class of ArrayList), which is an IEnumerator object capable of iterating over an ArrayList. Clients are required to refer to the returned object by the interface IEnumerator.

System.Net.WebRequest

WebRequest serves as a convenient base class for the .NET Framework's request/response model for accessing data from the Internet. This class encapsulates the details of connecting to the server, sending the request, and receiving the response. This means that an application can participate in request/response transactions in a protocol-agnostic manner using instances of the WebRequest class, while protocol-specific classes derived from WebRequest carry out the details of the request.

The static factory method WebRequest.Create creates protocol-specific descendants of WebRequest using the value of the URI passed in as argument. For example, when a URI beginning with "http://" is passed, an HttpWebRequest object is returned; when a URI beginning with "file://" is passed, a FileWebRequest object is returned.

By default, the .NET Framework supports "http://", "https://", and "file://" URI schemes. This is easily verified by looking at the section of the machine.config file in the .NET Framework installation's CONFIG directory:


This section implies that System.Net.HttpRequestCreator is responsible for creating WebRequest objects for request URIs beginning with "http" and "https", and System.Net.FileWebRequestCreator is responsible for creating WebRequest objects for request URIs beginning with "file". Both of these classes implement the System.Net.IWebRequestCreate interface, which contains only one public method:

>Figure 2. Factory Method pattern used for creating a WebRequest instance for "http"- and "https"-type URIs

Figure 2 illustrates the Factory Method pattern used to obtain a WebRequest instance for "http://"- and "https://"-type URIs. WebRequest.Create delegates creation of the appropriate WebRequest (the Product) object to an IWebRequestCreate object (the Creator). HttpRequestCreator (the ConcreteCreator) is then called to return an instance of HttpWebRequest (the ConcreteProduct). This design is interesting because it contains two Creators — WebRequest and IWebRequestCreate. Even though WebRequest is the Creator visible to the client, it is IWebRequestCreate that decides which derived object to return. The IWebRequestCreate interface is internal to the System.Net assembly by intention, so the only way to create an instance of WebRequest is by calling WebRequest.Create.

WebRequest.Create is a parameterized factory method -- it takes a parameter that identifies the type of object to create, and all objects created are of type WebRequest.

The System.Security.Cryptography Classes

The .NET Framework contains a rich and extensible collection of cryptography classes that you can use within your own programs, right out of the box. Cryptography classes are part of the System.Security.Cryptography namespace. These classes use the Factory Method pattern to decouple the choice of algorithms used from their specific implementations.

SymmetricAlgorithm, AsymmetricAlgorithm, and HashAlgorithm are the three roots of the class-inheritance hierarchy in the cryptography framework. These are abstract classes from which specific implementations of various encryption algorithms are derived. Each of these classes supports a static factory method called Create, which creates a particular instance of the cryptography class based on the type of algorithm requested. For example, the following line will instantiate a SymmetricAlgorithm-derived class called DESCryptoServiceProvider, which is the default implementation of the Data Encryption Standard (DES) algorithm in the .NET Framework.


The mapping between the string arguments and the instantiated classes can be found here.

Like WebRequest.Create, SymmetricAlgorithm.Create is a parameterized factory method; it takes a parameter that identifies the type of object to create, and all objects created are of type SymmetricAlgorithm. The use of a parameterized factory method here is important because it provides opportunities for extending the cryptography framework. For example, by adding a mapping for the RC5 algorithm (which does not currently exist in the .NET Framework SDK) that returns an instance of, say, RC5CryptoServiceProvider, one can add an RC5 implementation to the existing framework, and instantiate it using the following line:


System.Security.Policy.IIdentityPermissionFactory

The code-access security model implemented by the CLR requires every assembly to pass through a security checkpoint at the time of loading. This involves granting privileges to the code based on the origin of the assembly, digital signatures on the assembly, custom evidence attached to the assembly, requests made by the assembly, and so on. Evidence is presented by the assembly in the form of a System.Security.Policy.Evidence object, which is a collection of evidence objects (not Evidence objects) like System.Security.Policy.Url, System.Security.Policy.Zone, etc.

Each evidence object can possibly create an identity permission that is added to the grant set of the assembly. Therefore, for all pieces of evidence that implement the IIdentityPermissionFactory interface, the corresponding identity permission is obtained and added to the grant set of the assembly.

Figure 3. Obtaining a collection of IPermission objects for a collection of evidence objects

Figure 3 shows the class diagram for a case where an assembly presents only a System.Security.Policy.Url object, which is evidence of the URL from where the assembly originates. This evidence object is encapsulated in a System.Security.Policy.Evidence collection, which is then passed to the CreateIdentityPermission method of IIdentityPermissionFactory. The CreateIdentityPermission method has the following signature:


The .NET security framework calls the Factory Method CreateIdentityPermission on the appropriate implementation of IIdentityPermissionFactory to get an IPermission object that represents that piece of evidence. If multiple evidence objects are involved, then during policy resolution the runtime calls CreateIdentityPermission on all evidence objects that implement IIdentityPermissionFactory and grants the resulting identity permissions to the appropriate assembly in the form of a PermissionSet object, which is simply a collection of IPermission objects.

The Factory Method pattern used in the above framework abstracts out the policy resolution process to the highest level, where the client (typically the SecurityManager.Resolve method, as shown below) takes in a System.Security.Policy.Evidence object and gets back a PermissionSet object.


The mechanism of identifying the evidence types and creating IPermission objects representing those evidence types is completely transparent to the client.

ASP.NET HTTP Pipeline

Several instances of the Factory Method pattern can be found in the ASP.NET HTTP pipeline. The ASP.NET HTTP pipeline is a set of classes (in the System.Web namespace) that a web server uses to process an incoming HTTP request and return a response to the client. The pipeline takes care of session management, application pooling, caching, security, etc.

At every point in the pipeline, the objective is to advance the request processing by one step by either identifying the objects that can handle the incoming request, or by forwarding the request to objects capable of handling the request. Factory methods come into play when different types of objects need to be instantiated, based on the type of incoming request.

System.Web.HttpApplicationFactory

The entry point in the pipeline is the HttpRuntime object, which creates an instance of HttpContext for the particular request. HttpRuntime does not try to determine the type of HttpApplication object that will handle the request. Instead, it calls the static factory method HttpApplicationFactory.GetApplicationInstance, passing it the HttpContext instance just created. HttpApplicationFactory is an undocumented class that contains the logic required to determine the HttpApplication object capable of handling an incoming request. GetApplicationInstance uses the HttpContext instance to determine the virtual directory (i.e., application) that this request was sent to. If the virtual directory has been called before, then the HttpApplication object (or a derived ASP.Global_asax object, if Global.asax is defined) is returned from the pool of applications. Otherwise, a new HttpApplication object for that virtual directory is created and returned. Figure 4 shows the class diagram for instantiating an HttpApplication object capable of handling the request.

Figure 4. Fetching the appropriate HttpApplication object

HttpApplicationFactory.GetApplicationInstance is a parameterized factory method. It takes a parameter that identifies the type of product to create, and all products created are of type HttpApplication.

System.Web.IHttpHandlerFactory

Moving farther along in the pipeline, the HttpApplication instance just created needs the object that can handle the type of resource requested. This object is called a handler. To look up the proper handler, the HttpApplication first determines the factory that can create the handler. It does that by looking up the section of the machine.config file. If it's not found in machine.config, it looks up the application's web.config file, using the inherited configuration model. The section in these config files lists the handlers currently registered for the application. A part of this section is reproduced below.


This section shows the mapping between the type of resource requested and the class capable of creating a handler for that resource. If an .aspx page is requested, the System.Web.UI.PageHandlerFactory class will be called. Once this class has been located, HttpApplication invokes the GetHandler factory method on the IHttpHandlerFactory interface to retrieve a new instance of the handler class. All such factory classes must implement the IHttpHandlerFactory interface. These factory classes have no behavior except to dynamically manufacture new IHttpHandler objects using the GetHandler method. This method returns an IHttpHandler object, which in this example is the System.Web.UI.Page-derived class created from the .aspx file. For an .aspx file called SamplePage.aspx, the compiled class will be called ASP.SamplePage_aspx, as shown in Figure 5. This class serves as the endpoint for the request, and is responsible for populating the response buffer for that particular request.

Figure 5. Factory Method pattern used for obtaining the appropriate IHttpHandler object for a resource

IHttpHandlerFactory.GetHandler is a classic example of the Factory Method pattern, where IHttpHandlerFactory (the Creator) decides which IHttpHandlerFactory-derived object to create, PageHandlerFactory (the ConcreteCreator) creates ASP.SamplePage_aspx (the ConcreteProduct), which is returned as an IHttpHandler (the Product) object.

Conclusion

The Factory Method pattern is probably the most commonly found design pattern in the FCL. This article has presented a few notable instances of the use of this pattern in the FCL. If you begin to explore, you will probably find many more instances. Identifying those instances and understanding the motivation for their use can provide useful insights into your own designs.

References

  1. "Securely Implement Request Processing, Filtering, and Content Redirection with HTTP Pipelines in ASP.NET," by Tim Ewald and Keith Brown.
  2. Programming Microsoft ASP.NET, by Dino Esposito (Microsoft Press, ISBN 0735619034)
  3. Programming .NET Security, by Adam Freeman and Allen Jones (O'Reilly Publications, ISBN 0596004427)
  4. Essential .NET Volume 1: The Common Language Runtime, by Don Box (Addison-Wesley, ISBN 0201734117)
  5. Essential ASP.NET with Examples in C#, by Fritz Onion (Addison Wesley, ISBN 0201760401)
  6. Design Patterns: Elements of Reusable Object-Oriented Software, by Gamma et al. (Addison-Wesley, ISBN 0201633612)

Amit Goel has been developing object-oriented applications for several years. You can learn more about him at www.amitgoel.com.


Return to ONDotnet.com


你可能感兴趣的:(模式设计)