UML软件工程组织

灵活的.NET体系结构之简化配置服务(3)
 
作者:宁凯编译
 
来源:赛迪网
更改配置文件是比较容易的一件事情,这样就可以修改电子邮件的具体外表形式。请注意,如何使用string.Format(...) 将动态值替换到从配置文件中读取出来的模板中。另请注意,如何使用CDATA 部分将HTML嵌入到 XML 文件之间。

将公共静态服务转变成接口


在讲述如何实现这些方法之前,我们先来解释如何将一个静态方法转变成为一个接口。具体理由如下:AppServices 是一个接口集,其中的每个接口均表示一种服务。例如:


public AppServices
{
    public static IConfig getConfigurationService();
    pulbic static IFacgtory getFactoryService();
    public static ILog getLoggingService();
    ...any other application level services
}


我们来看看 IConfig可能包含的内容:


public interface IConfig
{
    public static getValue(string key);
      // throws an exception if the key is not found or has an 
      // empty string
    public static getValueHonourWhiteSpace(string key)
      // throws an exception if the key is not found
    public static getValue(string key, string default);
      // returns the default if the key is not found or has an 
      // empty string
    public static getValueHonourWhiteSpace(string key, string default)
      // returns the default if the key is not found
}


我们可能拥有另一支持XPath 的接口,如下所示:


public interface IConfigXPath
{
    //Xpath support
    public static getXPathValue(string key);
      // throws an exception if the key is not found or has an 
     // empty string
    public static getXPathValueHonourWhiteSpace(string key)
      // throws an exception if the key is not found
    public static getXPathValue(string key, string default);
      // returns the default if the key is not found or has an 
      // empty string
    public static getXPathValueHonourWhiteSpace(string key, string default)
      // returns the default if the key is not found
}


现在,我们继续操作,将会得到一个如下所示的实现:


public class DefaultConfig : IConfig, IXPathConfig
{
    ... Implements all the methods
}


现在对 AppServices 进行编码,如下所示:


public class AppServices
{
    private IConfig m_config = new DefaultConfig(); 
      // Potentially one can get this from a factory
    public static getIConfig() { return m_config; }
    ... and others
}


为什么要将静态方法转变成接口呢?这是因为对于一些情况,区分大小写是很有帮助的,但是对于另外一些情况,就没有必要区分大小写。而接口则可以很好地支持这两种情况:


public class CaseSensitiveConfig : IConfig
{
    //... implement your keys with case sensitive
}
public class CaseInsensitiveConfig : IConfig
{
    //... implement your keys with case insensitive
}


应用程序服务在运行时将根据实际情况返回一个适当的实现。如果您想知道如何实现,可以参考工厂服务。或者您可以阅读一些参考文献。下面我们演示一个更完整的示例:


public class CaseInsensitiveMultiFileConfig : IConfig
{
    // Implement your keys with case insensitivity and 
    // read from multiple config files
}


我们一直在强调配置文件的重要性,用不了多久,这种想法就会得到广泛应用。XML 配置文件将会随处可见。在一个团队中,可能需要将配置信息拆分成多个文件,以便增强团队成员之间的合作。

到目前为止,我们已经讨论了配置服务的优点以及如何使用配置服务的内容。这些内容应该说是经验的积累。接下来我们来看一些具体的实现。

实现


方法是足够简单了。所有的配置均可以在一个section中设置。诸如 DefaultConfig 这样的类可以读取此section中的数据内容。一旦获取了所需的数据内容,DefaultConfig 类就可以遍历XML文档中的每一个结点,并在数据字典或哈希表中为每一个关键字和值设置对应的一项。这里所提到的数据字典将可以响应客户端发送的关键字请求。

Section 处理器代码如下:


public class SimpleConfigurationSectionHandler :IConfigurationSectionHandler
    {
      public object Create(object parent, object configContext, XmlNode section)
      {
        return section;
      } // end of method
    } // end of class


DefaultConfig代码:


public class DefaultConfig : IConfig {
// keep a dictionary of values
private IDictionary m_keyValuePairs
// implement methods of IConfig using the above 
// dictionary details left to you
//  Constructor, where it reads your SimpleConfiguration 
// XML node using the section handler above
    public DefaultConfig()
    {
      // read the xml section for general config
      // Section name: SimpleConfiguration
      XmlNode xmlNode =
        (XmlNode)System
          .Configuration
          .ConfigurationSettings
          .GetConfig("SimpleConfiguration");
      if(xmlNode != null)
      {
      m_keyValuePairs = createDictionary(m_genConfigXmlNode);
      }
    }
}
// Here is the createdictionary
    private IDictionary createDictionary(XmlNode genConfigXmlNode)
    {
      IDictionary ht = new Hashtable();
      if(genConfigXmlNode != null && 
        genConfigXmlNode.ChildNodes != null &&
        genConfigXmlNode.ChildNodes.Count > 0)
    {
      // walk through each node
      // if it is a leaf node add it to the hash table
      // if it is not  continue the process
      walkTheNode(ht,"",genConfigXmlNode);
      }
      return ht;
    }
//  Here is how you walk the nodes recursively 
// to get your keys
private void walkTheNode(IDictionary ht, string parent, XmlNode node)
{
    if(node != null)
   {
     if (node.NodeType == XmlNodeType.Comment)
    {
      return;
    }
    if (node.HasChildNodes == false)
    {
      if (node.NodeType == XmlNodeType.Text)
      {
        // no children
        string leaf = node.Value;
        ht.Add(parent.ToLower(),leaf);
        // end of the recursive call
       return;
      }
     else if (node.NodeType == XmlNodeType.CDATA)
     {
        XmlCDataSection cdataSection = (
        System.Xml.XmlCDataSection)node;
        string leaf = cdataSection.Data;
        ht.Add(parent.ToLower(),leaf);
        // end of the recursive call
        return;
     }
     else
        {
        string key = parent + "/" + node.Name;
        string val = "";
        ht.Add(key.ToLower(), val);
        return;
        }
     }
     else
    {
      string newparent = parent + "/" + node.Name;
      // has children
      // walk all the children
     for(int i=0;i<node.ChildNodes.Count;i++)
     {
        // recursive call
       walkTheNode(ht,newparent,node.ChildNodes[i]);
     }
    }
  }
}


提示:编码时请特别关注如何处理 CDATA 和空文本结点。

在下一篇文章中,我们将在这种简单的配置服务基础之上对其进行扩展构成工厂服务FactoryService。工厂服务可以使您的体系结构变得更加灵活。
 

版权所有:UML软件工程组织