在Remoting客户端激活方式采用替换类以分离接口与实现
在我的博客里,《Microsoft .Net Remoting[基础篇]》一文提到了用替代类来取代远程对象元数据的方法。有朋友对此有些疑惑不解,所以专门Post本贴以解惑。
我们在部署Remoting分布式应用程序时,通常要求接口与实现完全分离,这样可以保证元数据的安全。也就是说,在客户端部署程序集时,不应包括远程对象的实现。然而,如果我们采用的是客户端激活方式,这个目的就很难实现。为什么呢?我们首先来看看客户端激活方式与服务端激活方式在客户端的区别:
如果是服务端激活方式,方法如下:
RemoteObject obj = (RemoteObject)Activator.GetObject(typeof(RemoteObject),ObjectUri);
其中RemoteObject为远程对象,ObjectUri是知名对象的Uri。从其实现来看,它需要知道远程对象的类型,但并不需要实例化。如果我们令该远程对象实现某接口,那么在GetObject方法中,完全可以传递接口的类型,并返回接口类型对象。
再看客户端激活方式,方法有两种:
一是调用Activator.CreateInstance()方法,很显然,这种方法要求的类型必须是类对象,因为它要创建具体实例。
第二种方法是采用RemotingConfiguration.RegisterActivatedClientType()方法。虽然在该方法中,也只需要传递其类型即可。但这种方法要求在注册了远程对象后,必须实例化该远程对象:
RemoteObject obj = new RemoteObject;
显然要求的类型也必须是类对象,道理不言而喻。
那么,当我们采用客户端激活方式时,怎样让接口与实现分离呢?在拙文《Microsoft .Net Remoting[基础篇]》中,提到有两种方法。一是使用工厂模式,再以服务端SingleTon激活模式来模拟客户端激活方式;另一种就是使用替代类。
所谓替代类,就是仍然创建和远程对象类相似的类对象。所谓的类似,是指远程对象类的命名空间、公共方法、字段和属性均相同,但方法和属性的具体实现却采用空语句或无关实现的方式。我们来看看远程对象类和替代类的代码:
远程对象类:
public class RemotingObj:MarshalByRefObject
{
public void Foo()
{
//在这里可以放上许多业务;
Console.WriteLine("Just a test.");
}
}
替代类:
public class RemotingObj:MarshalByRefObject
{
public void Foo()
{
return;
}
}
从代码中可以看到,远程对象类的方法Foo()中,有具体的业务实现,而在其对应的替代类Foo()方法中,却只有一条简单的Return语句。
在部署的时候,我们将实际的远程对象类放到服务端,而把替代类放到客户端,这样就可以保护远程对象类的元数据安全了。
为什么可以这样呢?我们要知道,在Remoting中采用客户端激活方式时,虽然在客户端创建了远程对象实例,实际上此时只是创建了一个代理类而已,它真正指向的还是在服务端创建的远程类对象。我们提供这样一个替代类,就是用一种“欺骗”的方式,瞒过客户端,使其可以顺利地实例化这个代理类。而当我们在客户端调用这个对象的方法:
obj.Foo();
此时它将通过代理类去读取远程服务端的真实地远程对象。也就是说,此时的Foo()方法并非替代类的return语句,而是真正远程对象类的业务实现。
这种方法其实就是一个Trick,看似巧妙,不过在大型的应用时,其缺陷也是很明显的。由于我们要为每个远程对象都要实现该具体类,因此当远程对象众多时,带来的工作量也是可观的。这种方法也还算不上是真正的分布式应用。因此,大家如果要使用客户端激活方式,同时要求接口和实现分离,最好还是采用工厂模式,并以服务端SingleTon激活模式来模拟客户端激活方式。
附示例源代码下载。