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.