类厂服务的主要功能是简化创建具体工厂任务,使工厂访问代码和工厂创建代码解藕。类厂服务是以抽象工厂模式为基础,并且在其上进行再综合。
    在正式进入正题之前,为了方便后面的叙述,先要澄清一些概念,把上下文(Context)搭建起来,然后,我们再在这个上下文中进行讨论。

    首先是两个基本定义:族和系列。(这两个概念是我自创的,不知道常用的术语是什么,知道的朋友请留言告诉我:))
(1)族   -- Category,就是指特定的一类物品。比如服装是一种Category,食物又是一种Category。
        在程序设计和实现中,一个族对应着一个抽象工厂,而我们的当前系统可能会牵涉到多个族。 
 (2)系列 -- Style,指某个族中的某个风格。比如可以把服装的品牌当作Style,一个品牌对应一种Style。

    为了更容易理解,看看IFactory的继承结构就清楚了:

/// <summary>
     /// IFactory 抽象工厂接口的基础接口。所有的抽象工厂接口均从此接口继承。 
     /// </summary>
     public interface IFactory
     {
         /// <summary>
         /// 族名称 ,在抽象工厂这一层就可以确定CategoryName了
         /// </summary>        
         string CategoryName{get ;}//比如是"服装",还是"食物"        /// <summary>
         /// 系列名、风格名,需要到具体工厂那一层才确定的了
         /// </summary>
         string StyleName{get ;}  //比如耐克、李宁
     }    //抽象工厂IFoodFactory ,族已经确定
     public interface IFoodFactory :IFactory
     {
         Apple GetApple() ;
         Rice  GetRice() ;
     }    //具体工厂ChineseFoodFactory ,风格(系列)已经确定
     public interface ChineseFoodFactory : IFoodFactory
     {
     }    public interface JapaneseFoodFactory : IFoodFactory
     {
     }


    

    其次,需要给出当前系统基本假设
(1)不会引入新的族(Category)。因为只要引入新的族,系统肯定需要进行大的修改。因为原系统对新Category一无所知。
(2)系统不会混用同一族中不同的风格。比如不会有一个人上面穿李宁的运动服,下面却穿耐克的鞋子:)

    接下来,我们就能讨论类厂服务了。类场服务的目的主要有两个:
(1)如果系统原先使用Category A族中Style A系列的产品,当系统需要更换到Style B,应该通过修改配置做到(比如把具体工厂A改为具体工厂B),或者仅需要修改相关的几行代码。
(2)隐藏远程工厂与本地工厂的区别。也就是说,系统中不用关心所引用的工厂实例是本地的,还是remoting的。这个也可以通过更改配置来把原来使用本地对象配置为使用远程对象,而我们的系统根本不受影响。

    为了获取具体的工厂实例,需要得到诸如该具体工厂类型名、位置等信息,这些信息通过FactoryInformation来封装。

/// <summary>
     /// FactoryInformation 通过反射创建对应的工厂时,需要这些相关信息
     /// (1)当为非远程时,typeName为具体工厂类型名,如果为远程,typeName通常抽象工厂接口类型,如typeof(IFoodFactory)。
     /// (2)assemblyName参数对现有族中添加一个新系列提供了支持,可以将新系列放在一个独立dll中,然后,修改配置,
     ///    使系统从此使用新系列的对象。
     /// </summary>
     public class FactoryInformation
     {
         private string factoryID ;//类厂唯一标识
         private string typeName  ;//类厂的类型全名称
         private string assemblyPath ;//类厂所在配件
         private string location  ;//http://RemoteServer1/ClassFactory.rem
         private bool   isInCurrentAssembly = true ;//是否在当前exe配件中        public FactoryInformation(string factory_ID ,string type_Name ,string assPath ,string loc ,bool isInCurAssem)
         {
             this.factoryID             = factory_ID ;
             this.typeName             = type_Name ;
             this.assemblyPath         = assPath ;
             this.location             = loc ;
             this.isInCurrentAssembly = isInCurAssem ;
         }        #region property      
         #region IsRemoting
         public bool IsRemoting  //是否为远程
         {
             get
             {
                 if((this.location == null) ||(this.location.Trim() == ""))
                 {
                     return false ;
                 }                return true ;
             }
         }
         #endregion        #region IsInCurrentAssembly
         public bool IsInCurrentAssembly //是否在当前的配件中
         {
             get
             {
                 return this.isInCurrentAssembly ;
             }
         }
         #endregion
         #endregion
     }


    上面的代码省略了一些省略简单属性。其余的已经完全可以通过注释来解释清楚了,需要指出每个工厂都有一个唯一标志factoryID,根据这个标志,我们可以要求类厂服务基础设施来为我们创建指定的工厂对象。
   
     类厂服务是一个静态类,外貌如下:

public class ClassFactoryService
     {        
         public static void Initialize(IFactoryInfoGetter fiGetter) ;    
         public static IFactory GetFactory(string factoryID) ;        
     }

 其中只有两个静态方法,一个用于初始化,一个用于获取工厂对象。
    初始化需要有个IFactoryInfoGetter的参数,IFactoryInfoGetter用于获取指定工厂的FactoryInformation, 通常将所有的FactoryInformation存放于配置文件中。IFactoryInfoGetter接口定义如下:

public interface IFactoryInfoGetter
     {
         FactoryInformation GetFactoryInformation(string factoryID) ;    
     }    ClassFactoryService.Initialize一般在系统启动的时候调用。这样以后就可以正常的使用类厂服务了。下面看看关键的GetFactory方法的实现,看它是如何达到前面提到的类厂服务的两个目的的。
 public static IFactory GetFactory(string factoryID) 
         {
             if(ClassFactoryService.factoryInfoGetter == null)
             {
                 return null ;
             }            FactoryInformation factoryInfo = ClassFactoryService.factoryInfoGetter.GetFactoryInformation(factoryID) ;
             if(factoryInfo == null)
             {
                 return null ;
             }            Type factoryType = null ;
             //远程
             if(factoryInfo.IsRemoting)
             {            
                 factoryType = Type.GetType(factoryInfo.TypeName) ;//此时factoryType通常为抽象工厂接口类型
                 return (IFactory)Activator.GetObject(factoryType ,factoryInfo.Location) ;
             }            //本地,在当前配件中
             if(factoryInfo.IsInCurrentAssembly)
             {
                 factoryType = Type.GetType(factoryInfo.TypeName) ;
                 return (IFactory)Activator.GetObject(factoryType ,null) ;
             }            //本地,不在当前配件中,但目标配件已被加载
             factoryType = Type.GetType(factoryInfo.TypeName) ;
             if(factoryType != null)
             {
                 return (IFactory)Activator.GetObject(factoryType ,null) ;
             }            //本地,不在当前配件中,而且未加载目标配件
             Assembly destAssembly = Assembly.LoadFrom(factoryInfo.AssemblyPath) ;
             if(destAssembly == null)
             {
                 return null ;
             }            factoryType = destAssembly.GetType(factoryInfo.TypeName) ;
             return (IFactory)Activator.GetObject(factoryType ,factoryInfo.Location) ;
         }


    注释已经很好的说明了发生的一切,其中最主要的就是使用了简单的反射技术和Remoting技术。到这里,我们的任务已经完成了,但是有些注意事项需要提出来:
(1)当在远程服务器上发布工厂类时,客户端可以通过下面三种方式得到其类型信息:
a.  把远程对象的接口程序集部署到客户端,即客户端可以得到其对应的抽象工厂接口信息。
b. 把实际的工厂类部署到客户端。
c. 在客户端部署一组"空类" ,这组类实现了抽象工厂接口,并且从MarshalByRefObject继承。可以通过SoapSuds.exe完成。(推荐)

(2)如果类工厂配置为remoting,那么该类工厂产生的所有物品都必须实现MarshalByRefObject。

    关于类厂服务,我自己已经实现的就这么多,还有很多思路没有实现出来,等那些新想法成型后再拿出来和大家讨论。