UML软件工程组织

灵活的.NET体系结构之统一的工厂服务(2)
作者:Satya Komatineni著 宁凯编译 本文选自:赛迪网 2003年01月22日

 

现在,您所拥有的函数可以实现通用目标,该函数能够为您提供一个与指定符号名相对应的对象。此实用函数被称作工厂服务。不过这仅仅是工厂服务的简单框架。我们将以此为基础作进一步的扩展,以便能够覆盖更多的情况,争取实现最大限度的利用

形式化的工厂服务

我们将需求形式化为一个工厂服务接口。

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 标记。

(责任编辑 Sunny)




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