WindowsDevCenter.com
oreilly.comSafari Books Online.Conferences.

advertisement


AddThis Social Bookmark Button

.NET Application Services Part 2: A Unified Factory service

by Satya Komatineni
09/16/2002

This is the second article in a series on building a flexible .NET architecture. The first article offered a simplified configuration service. That article introduced a simple idea for most of the configuration needs of an application.

In that article, I showed that given a configuration file that looks like the following:


<GeneralConfiguration>
<section1>
<key1>value1</key1>
</section1>
<section2>
<key name="x">value_for_x</key>
<key name="y" a1="10" a2 ="15">
<a3>16</a3>
<a4>17</a4>
</key>
</section2>
</GeneralConfiguration>

You can access values from this config file using the following descriptive sequence of calls:


// get a class responsible for implementing IConfig
     IConfig cfg;

// reading a simple key
     cfg.getValue("/section1/key1") -> value1

// reading a key with reserved name attribute
     cfg.getValue("/section2/key/x") -> value_for_x

// reading a key with reserved name attribute and its 
// attribute/child nodes
     cfg.getValue("/section2/key/y/a1") -> 10
     cfg.getValue("/section2/key/y/a3") -> 16

There are a number of benefits to using IConfig:

  • Offers ability to access hierarchical informational structures with a simple directory-like API.
  • Does not replace XPath, by any means, but merely supplements it with some workable conventions.
  • Is simple.
  • Allows default values.
  • Can distinguish between mandatory values and optional values.
  • Can be case-insensitive for keys while preserving the case for the key values.
  • With certain assumptions, can treat attributes and children similarly.

Factory Service: Journey Into the Domain of Architecture

Related Reading

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

I think that the key to building large structures starts with simple ideas. Sometimes I also fancy that the more complex a usable structure is, the more likely that it is constructed out of simple ideas. In other words: complexity cannot be built from complexity. My goal in this section is to show you how the simple-minded configuration service that we have dealt with so far will give rise to yet another simple application-level service: a factory service.

So What is a Factory Service, Anyway?

Languages have a tendency to formalize popular programming practices (often called patterns) and codify them into language constructs. For example, Java introduced the keyword interface into the language, which, prior to Java, was merely a popular underground activity in the C++ world.

C# follows suit and also has the keyword interface. (C# has formalized event handling as well, through a keyword called delegate). To understand factory services, one has to understand interfaces. Although I will explain interfaces in some detail here, please refer to your favorite book on C# to read up on them. It is time well spent, as interfaces are the backbone of the architecture.

A Look at Interfaces

An interface is like a class with a set of methods. So, if someone hands you an object and you know the interface it implements, you can call those methods in a typesafe way. That is a lot of talk, but it is quite simple, if you examine the following code:


//Let us define an interface

interface LoggingInterface
{
     public void logMessage(String message);
}

//Let's see how we can use this interface

public void myLoggingFunction( Object o)
{
     if (o is LoggingInterface)
     {
          LoggingInterface log = (LoggingInterface)o;
          log.message("test message");
     }
     else
     {
          // sorry not sure of the type of this object
          throw new Exception("Unexpected type");
     }
}

Now the question is, when you call the logMessage function, who is actually logging the message? The interface above has no code to write this message. How is that done? You need to do the following to make that happen:


public class FileLogger : LoggingInterface
{
     private FileOutputStream fs = null;
     public FileLogger(string filename)
     {
          fs = new FileOutputstream(filename);
     }

     // implement the following method to complete the 
	 // contract required by LoggingInterface
     public void logMessage(String message)
     {
          fs.println(message);
     }

     public close()
     {
          fs.close();
     }
}

Now you can do, in your code:


     FileLogger     fileLogger = new FileLogger("myfile");
     myLoggingFunction(fileLogger);

As you are getting accustomed to this style of coding, a fancy pants consultant like me could descend on your happy blanket and make it now a requirement that all logging needs to go to the event logging supported by the operating system. So, in a hurry, he creates a new class, as follows:


public class EventLogger : ILog
{
     ... other code
     public void logMessage(string message)
     {
          ..  do the needful
     }
}

Now you have to change your happy blanket from:


     FileLogger  fileLogger = new FileLogger("myfile");
     myLoggingFunction(fileLogger);
to

     EventLogger eventLogger = new EventLogger(..);
     myLoggingFunction(eventLogger);

Then you start wishing for a utility function that can give you the right logging interface:


     LoggingInterface log = MyUtility.getLogger();
     myLoggingFunction(log);

So now you start writing this MyUtility.getLogger() function as follows:


     public LoggingInterface getLogger()
     {
          return new FileLogger("myfile");
          // return new EventLogger();
     }

Now you are fancy-pants-proof. But still there is a nagging feeling, the sort that hovers around when you haven't had your cup of coffee after a satisfying feast. So you start fidgeting to spot your cup of Java (sorry, couldn't resist). At this time, the configuration service that you have so long ignored flashes by. You imagine a config file as follows:


     <request name="logger'>
          <classname>EventLogger
     </request>

You also remember that in a good number of not-so-macho-interpreted languages, it is not difficult to instantiate a class if you know its name and pedigree (package, assembly, etc). Armed with information, you modify your getLogger() utility function to the following:


     public LoggingInterface getLogger()
     {
          IConfig cfg = ...
          string loggerClassname = cfg.getValue
("/request/logger/classname","EventLogger");

          Object o = instantiateAnObjectForItsClassname(
		      loggerClassName);
// code is not provided for this function
          LoggingInterface log = (LoggingInterface)o;

          return log;
     }

Now your quest is complete (almost). You are able to instantiate a logging class dynamically using a config file. Then, as your needs grow, you want this capability to instantiate dynamic objects for all sorts of classes and not just the logger. So you modify the utility function yet again:


     public static Object getObject(string interfacename)
     {
          IConfig cfg = ...
          string interfaceClassname = cfg.getValue("/request/" +
interfacename + "/classname");

          Object o = instantiateAnObjectForItsClassname(
		      loggerClassName);
// code is not provided for this function
          return o;
     }

Now you have a general purpose function that can give you an object, given a symbolic name. This utility function is called a factory service. This is a bare-bones factory service. We will expand on this to cover more cases so that you can take advantage of this metaphor.

Factory Service Formalized

So let us formalize our need for a factory into a factory service interface.


public interface IFactory
{
     public Object getObject(string symbolicname):
}

So, essentially, a factory service is a service that returns to you an object given a symbolic name.

Pages: 1, 2

Next Pagearrow