Exemplifying the Factory Method Pattern inside the .NET Framework
page 4 of 6
by Xianzhong Zhu
Feedback
Average Rating: 
Views (Total / Last 10 Days): 38829/ 115

Digging Further

For now, everything seems better. However, we still worry about the creation of the concrete factory objects. For example, when the WebRequest object is to be created, the related factory class object should be created beforehand in the system.

Listing 11

IWebRequestCreate webRequestCreate = new 
 HttpRequestCreator();

Different from common objects, the types of WebRequest might change at any moment, which correspondingly results in the related factory types changing accordingly. In this case, even if we put the code of creating the factory in a special module as described above, it still lacks flexibility. After all, as a summit framework, the design tenet of .NET framework is to facilitate the use of the class libraries for developers as far as possible, as well as to deal with various extensibilities. So, we can not settle for the present design. Well, any other better ideas?

In fact, it is only through digging further into the implementation of all WebRequest related objects that we find out how the Microsoft architects have racked their brains in scheming the design of the WebRequest class. So, have you caught on to the reason that the abstract WebRequest class introduces a static method Create? What on earth hides behind the scene? For this, we have to dig further into the inner implementation of this method.

Listing 12

public static WebRequest Create(Uri requestUri)
 {
   if (requestUri == null) 
  {
     throw new ArgumentNullException(”requestUri”);
   }
    return Create(requestUri, false);
 }

Now, as you have seen, another private static method named Create is called inside this method. OK, let us continue to follow up the scene:

Listing 13

private static WebRequest Create(Uri requestUri, bool useUriBase)
 {
  string LookupUri;
  WebRequestPrefixElement Current = null;
  bool Found = false;
 if (!useUriBase) {
       LookupUri = requestUri.AbsoluteUri;
  }
  else {
      LookupUri = requestUri.Scheme + ‘:’;
  }
 int LookupLength = LookupUri.Length;
  ArrayList prefixList = PrefixList;
 for (int i = 0; i < prefixList.Count; i++)
  {
        Current = (WebRequestPrefixElement)prefixList[i];
       // See if this prefix is short enough.
        if (LookupLength >= Current.Prefix.Length)
   {
            // It is. See if these match.
            if (String.Compare(Current.Prefix,0,LookupUri,0,Current.Prefix.Length,true,
  CultureInfo.InvariantCulture) == 0)
    {
                   Found = true;
                   break;
            }
         }
  }
     if (Found)
  {
         return Current.Creator.Create(requestUri);
    }
   throw new NotSupportedException(SR.GetString(SR.net_unknown_prefix));
 }

In a word, the above method retrieves the WebRequestPrefixElement object meeting the specified condition in an ArrayList structure according to the value of the passed parameter of Uri. If found, then invoke the method Create of property Creator of object WebRequestPrefixElement, and then create and return the proper WebRequest object; or else, throw a related exception.

Now, we have to make clear two questions:

1. What is the definition of class WebRequestPrefixElement? And what is the type of property Creator of class WebRequestPrefixElement?

2. What on earth does the PrefixList object hold?

Well, let us first look at the definition of class WebRequestPrefixElement.

Listing 14

internal class WebRequestPrefixElement
{
  public string Prefix;
  public IWebRequestCreate Creator;
public WebRequestPrefixElement(string P, IWebRequestCreate C)
{
  Prefix = P;
  Creator = C;
}
}

Now, we have known that class WebRequestPrefixElement is responsible for establishing a one-to-one mapping between the prefix Prefix of Uri and the IWebRequestCreate typed object Creator, which is performed through the constructor of class WebRequestPrefixElement.

For now, we have also made clear the function of the sentence "Current.Creator.Create(requestUri)" inside the private static method Create of class WebRequest. In detail, it bears the responsibility of finding the proper IWebRequestCreate typed object in the specified Uri, then invokes its factory method to create the final object-- WebRequest. Well, which factory method has been invoked? (What is the IWebRequestCreate object that property Current represents?)

In fact, as is hinted in the above code, the value of Current is just the IWebRequestCreate object obtained from the structure prefixList, while the value of this prefixList inside method Create is just the PrefixList property defined in class WebRequest.

Next, let us continue to examine the definition of property PrefixList.

Listing 15

private static ArrayList PrefixList
{
  get
  {
    if (s_PrefixList == null)
    {
      lock (typeof(WebRequest))
      {
        if (s_PrefixList == null)
        {
          GlobalLog.Print(
            ”WebRequest::Initialize(): calling ConfigurationSettings.GetConfig()”);
          ArrayList prefixList =
      (ArrayList)ConfigurationSettings.GetConfig(”system.net/webRequestModules”);
 
          if (prefixList == null)
          {
            GlobalLog.Print(”WebRequest::Initialize(): creating default settings”);
            HttpRequestCreator Creator = new HttpRequestCreator();
 
            // longest prefixes must be the first
            prefixList = new ArrayList();                            
            prefixList.Add(new WebRequestPrefixElement(”https”, Creator));
            prefixList.Add(new WebRequestPrefixElement(”http”, Creator));
            prefixList.Add(new WebRequestPrefixElement(”file”, 
              new FileWebRequestCreator())); //
          }
          s_PrefixList = prefixList;
        }
      }
    }
    return s_PrefixList;
  }
  set
  {
    s_PrefixList = value;
  }
}

As you have seen, inside the Get accessor of property PrefixList, a series of judgments and initializations have been performed. The most important work here is adding three elements which are all of type of the WebRequestPrefixElement object, through which a one-to-one mapping is established between Uri and IWebRequestCreate in the prefixList property.

Listing 16

prefixList.Add(new WebRequestPrefixElement(”https”, Creator));
prefixList.Add(new WebRequestPrefixElement(”http”, Creator));
prefixList.Add(new WebRequestPrefixElement(”file”, new FileWebRequestCreator()));

Inside property prefixList, the first two elements related factory types are both of type of HttpWebRequestCreator, while the third element related factory type is FileWebRequestCreator. The two are just the two concrete factory classes that implement interface IWebRequestCreator.

Now, we have further realized that during the course of invoking WebRequest’s static method Create(), the system will, according to the passed Uri object, retrieve from inside prefixList the WebRequestPrefixElement typed object whose prefix matches the uri inside property PrefixList( through the String.Compare() method). If found, then invoke the corresponding factory class to create related WebRequest instance according to the one-to-one mapping.

Now, let us look back to see the related code to create the WebRequest object.

Listing 17

WebRequest myRequest = 
 WebRequest.Create("http://www.aspalliance.com");

According to the analysis above, the interior of the single line of code has implemented the following steps:

1. Pass the string "http://www.aspalliance.com" to the private static method Create() of class WebRequest.

2. Assign the value of the private property PrefixList of class WebRequest to the local variable, i.e. the prefixList object. In this case, the Get accessor of property PrefixList is called which can initialize the PrefixList object, and adds the default Uri and the value of the IWebRequestCreate type into the PrefixList.

3. Resolve the passed Uri, and get the value "http," where the related IWebRequestCreate object is the HttpWebRequestCreator object.

4. Invoke the Create() method of the HttpWebRequestCreator object, creating and returning a HttpWebRequest object.

Next, let us look at the timing diagram of the static method Create of class WebRequest, as shown in Figure 8 below.

Figure 8 - Timing diagram to create object WebRequest

Next, we consider the case of extensibility. If the derived classes of WebRequest are not only limited to the two classes HttpWebRequest and FileWebRequest, there is still a third subclass named FtpWebRequest (with the Uri being "ftp" and the related factory class being "FtpWebRequestCreator"), then how can we create the new subclass?

According to the analyses above, due to the fact that inside the get accessor of property PrefixList no other WebRequestPrefixElement objects are added, method Create cannot find the appropriate IWebRequestCreate factory object if the passed prefix for Uri is "ftp." Do we have to modify the property Prefix related code inside class WebRequest in the case that we need to extend the new subclasses for class WebRequest? If so,you may be astonished, all the above factory classes and related "skills" may seem clumsy and antic!

In fact, it is not the real case. By digging further, you can easily find out some traces which imply some ingenious tips and skills. For example, as you may still remember, during the course of the creation of WebRequest, Microsoft architects vexatiously introduced class WebRequestPrefixElement and an ArrayList typed PrefixList object. Is there some occult hiding behind this?

Well, you know that the ArrayList object permits new elements being added into it. The good thing is that the WebRequest class just contains a public static method named RegisterPrefix which is used to register the WebRequestPrefixElement object.

Listing 18

public static bool RegisterPrefix(string prefix, IWebRequestCreate creator)
{
 bool Error = false;
 int i;
 WebRequestPrefixElement Current;
 
 if (prefix == null)
 {
      throw new ArgumentNullException(”prefix”);
 }
 if (creator == null)
 {
      throw new ArgumentNullException(”creator”);
 }         
 
 lock(typeof(WebRequest))
 {           
      ArrayList prefixList = (ArrayList) PrefixList.Clone();
      i = 0; 
      while (i < prefixList.Count)
  {
           Current = (WebRequestPrefixElement)prefixList[i];
           if (prefix.Length > Current.Prefix.Length)
   {
                 // It is. Break out of the loop here.
                 break;
           }
           if (prefix.Length == Current.Prefix.Length)
   {
        // They’re the same length.
          if (String.Compare(Current.Prefix, prefix, true, 
                CultureInfo.InvariantCulture) == 0)
    {
        // …and the strings are identical. This is an error.
               Error = true;
               break;
                 }
          }
            i++;
      }
      if (!Error)
  {
           prefixList.Insert(i, new WebRequestPrefixElement(prefix, creator));
           PrefixList = prefixList;
      }
 }
 return !Error;
}

Here, if the passed prefix cannot be found in the structure PrefixList, the new prefix and object IWebRequestCreate are inserted into the PrefixList structure. Through this means, new factory classes can be added dynamically. For this, consider the following.

Listing 19

WebRequest.RegisterPrefix(“ftp”,new 
 FtpWebRequestCreator());
WebRequest ftpRequest = 
 WebRequest.Create(“ftp://www.aspalliance.com”);

With this mode, we can finally achieve the target of decoupling the concrete factory class, concrete product class, and the static method Create() of class WebRequest.

In the .NET Framework, it seems rather complex to create a class WebRequest related object, not to mention the concrete implementation of class WebRequest. In essence, however, Microsoft architects utilized the Factory Method pattern to get over the inside knots one after another. Moreover, to make the class library more feasible, universal and extensible, another mapping-like class WebRequestPrefixElement and the ArrayList object are introduced. At the same time, set the concrete factory class as internal, and encapsulate it into the static method of the abstract product base class WebRequest.


View Entire Article

User Comments

Title: Images missing   
Name: James
Date: 2013-01-21 4:01:32 PM
Comment:
Hey all figure images are missing. Can you fix this??
Title: Amazing   
Name: Nimesh
Date: 2010-08-04 9:11:39 PM
Comment:
Hi Xianzhong Zhu,
I can stop praising you for this article. It has answered so many of my questions along the way of learning new things. You should never stop writing such interesting articles man.

Carry on mate,

Nimesh






Community Advice: ASP | SQL | XML | Regular Expressions | Windows


©Copyright 1998-2020 ASPAlliance.com  |  Page Processed at 2020-07-04 8:28:08 PM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search