现在,您所拥有的函数可以实现通用目标,该函数能够为您提供一个与指定符号名相对应的对象。此实用函数被称作工厂服务。不过这仅仅是工厂服务的简单框架。我们将以此为基础作进一步的扩展,以便能够覆盖更多的情况,争取实现最大限度的利用 形式化的工厂服务 我们将需求形式化为一个工厂服务接口。 public interface IFactory { public Object getObject(string symbolicname): }
所以,工厂服务将返回指定符号名所对应的对象。 使用缓存增强工厂服务 假设这样一种情况:现在有两个客户端正在尝试使用工厂服务登录到应用程序。具体情形如下所述: //client1 IFactory fact; LoggingInterface log = (LoggingInterface)fact.getObject( "logger"); log.logMessage("Message from client1"); //client2 IFactory fact; LoggingInterface log = (LoggingInterface)fact.getObject( "logger"); log.logMessage("Message from client2");
同时假定使用FileLogger 作为我们的实现工具。所以现在的问题就是,您预期在应用程序中会有多少 FileLogger对象呢?如果两个客户端能够共享同一对象,这是非常理想的结果。因此,工厂服务就应该将这一要求考虑在内,对所请求的对象进行缓冲存储。但是,工厂服务如何才能知道要对该对象进行缓冲存储?这就需要为工厂服务提供必要的线索,以便使其明确该类在整个应用程序中 只有一个实例。 要做到这一点,可以采用以下两种方式: 一种方法就是更改配置,如下所示: ·<request name="logger'> · <classname>EventLogger</classname> · <instance-count>single|multiple</instance-count> ·</request>
现在工厂服务就能够确定是否需要将此 instance-count 读取到缓存。 另一种实现方法如下所示: ·public class FileLogger : LoggingInterface, · SingeInstanceInterface ·{ · ... ·}
SingleInstanceInterface是一个接口标记,FileLogger使用该接口标记来告知工厂服务该对象设计为单实例,因此工厂服务可以放心地对此对象进行缓冲存储。 以上这两种方法有一个很重要的不同之处。 例如,如果假定支持多实例,您很可能会将 FileLogger 设计成不对线程进行检测。同时,负责管理配置文件的人员却将“多实例”错误地更改为“单实例”。这样的话,就可能会引发线程问题。 一个类确定为单实例还是多实例成为设计时一个很重要的限制。我个人认为,将其表述为一个接口标记更为合适。接口标记仅仅只是一个名称而已,它主要用来说明所提到的接口中没有任何方法。 接下来,我们来定义缓存描述。 public interface SingleInstanceInterface {} public interface MultiInstanceInterface {}
如果我们所提及的类并没有实现上述的任何一个接口,则默认为MultipleInstanceInterface接口。 使用初始化改善工厂 到目前为止,我始终未提及 FileLogger的一个很重要的细节。下面,我们再来看一下配置文件吧: <request name="logger'> <classname>FileLogger </request>
您已经开始使用如下所示的代码: IFactory fact; LoggingInterface log = (LoggingInterface)fact.getObject( "logger"); log.logMessage("Message from client2");
现在有这样一个问题:FileLogger。如何才能知道要写入到哪一个文件呢?因为工厂并不负责将某一参数传递给FileLogger类。接下来我们来研究一种很明显的情形。我们为什么不选择通过修改工厂服务来将参数传递给getObject 方法,以便此方法能够将参数传递给FileLogger?为了实现上述目标,上面的函数应该为: IFactory fact; LoggingInterface log = (LoggingInterface)fact.getObject( "logger", "filename"); log.logMessage("Message from client2");
如果想将日志更改为 EventLogger又该怎么办呢? 实际上,这些附加参数可能会违反工厂服务的约定。所以,我们将不得不使用另一可选方法。我们首先来看看第一个示例: public class FileLogger { FileLogger(string filename); FileLogger() // default constructor, one that the // factory is interested in { // read the filename from config IConfig cfg.. string filename = cfg.getValue("/Logging/filename",null); this(filename); } }
要实现上述所描述的内容,您还需要具备以下配置: <request name="logger'> <classname>FileLogger</classname> </request> <Logging> <filename>abc</filename> </Logging>
现在我们要回过头来解决前面遗留的问题:配置信息作为FileLogger的一部分在其他地方也将会提到。那么。我们又打算如何来解决该问题呢?首先,我们将配置设为: <request name="logger'> <classname>FileLogger</classname> <filename>abc</filename> </request>
下面使用下列接口来处理FileLogger 类: public interface InitializableInterface { public void initialize(string requestname, IConfig cfg); } public class FileLogger : LoggingInterface // main job ,SingleInstaceInterface // only one copy exists ,InitializableInterface // we will see now what this is { private string filename; FileLogger(); public void initialize(string requestname, IConfig cfg) { filename = cfg.getValue("/request/" + requestname + "/filename",null); .. other initialization stuff } }
与工厂相关的接口 现在,我们来对工厂服务所涉及的接口进行简单的总结。请注意:为了方便起见,以下将使用"I" 来代表接口。 public interface IFactory { // Instantiates an object and returns it public object getObject(string symbolicName); .. More methods to be covered in subsequent articles } public interface ISingleInstance {} // Indicates to the factory that the intantiated object // needs to be cached public interface IMultiInstance {} // Object can not be cached public interface IInitializable { // used to read further unified configuration information public void initialize(string symbolicname); }
使用上述工厂服务实现组件 我们可以将组件看作是一类方法的集合。 Public interface IComponent1 { ReturnType1 method1(arg1, arg2, etc.); ReturnType2 method2(arg1,arg2, etc..) }
实现此组件的方法如下所示: Public class MyComponentImplementation : IComponent1, ISingleInstance { // any local variables you may have ReturnType1 method1(arg1, arg2, etc.) { .. Method1 implementation } ReturnType2 method2(arg1,arg2, etc..) { .. Method2 implementation } }
可以使用下列配置文件来指定运行时的实现。 <request name="IComponent1> <type>MyComponentImplementation,MyAssembly</type> </request>
这里的“type”是.NET用以指定类名的一种机制。一旦上述的配置正确,就可以照此来使用组件: Ifactory fact; IComponent1 compInterface = (Icomponent1)fact.getObject ("IComponent1"); compInterface.method1(..); compInterface.method2(..);
这些组件的具体优势 首先,这些组件可以保证类型安全。这就是说参数能够使用类型安全的方法来调用组件之上的方法。一旦获得了组件,利用符号名,该组件之上的方法就可以保证类型安全。 其次,这些组件是独立存在的。您可以根据需要在应用程序中的任一位置申请和使用某一组件。不过,这里有这样一个假定:将组件作为无状态方法集来实现。如果想使用有状态方法,将不能实现 ISingleInstance 标记。 |