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 |
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:
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 <webRequestModules> section of the
machine.config file in the .NET Framework installation's CONFIG directory:
<webRequestModules>
<add prefix="http" type="System.Net.HttpRequestCreator, .../>
<add prefix="https" type="System.Net.HttpRequestCreator, .../>
<add prefix="file" type="System.Net.FileWebRequestCreator, .../>
</webRequestModules>
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:
WebRequest Create(Uri uri);
|
>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.
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.
// Return an instance of DESCryptoServiceProvider
SymmetricAlgorithm s = SymmetricAlgorithm.Create("DES");
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:
// Return an instance of RC5CryptoServiceProvider
SymmetricAlgorithm s = SymmetricAlgorithm.Create("RC5");
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:
System.Security.IPermission CreateIdentityPermission(
System.Security.Policy.Evidence evidence);
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.
Evidence evidence = new Evidence();
.....
PermissionSet permissionSet = SecurityManager.ResolvePolicy(evidence);
The mechanism of identifying the evidence types and creating IPermission objects
representing those evidence types is completely transparent to the client.
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 <httphandlers> 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 <httpHandlers> section in these config files lists the handlers currently registered
for the application. A part of this section is reproduced below.
<httpHandlers>
<add verb="*" path="*.aspx" type="System.Web.UI.PageHandlerFactory" />
<add verb="*" path="*.ashx" type="System.Web.UI.SimpleHandlerFactory" />
.....
</httpHandlers>
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.
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.
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
Copyright © 2009 O'Reilly Media, Inc.