** Effective C#: 4.使用类厂(Class Factory)模式实现 **
基于接口的客户激活远程对象(下)
陈铭 Microsoft C#/.NET Asia MVP
难度:7/10 条款3
至此,关于 SAO 对象编译和发布的问题已经解决。再让我们回过头来看一下关于 CAO 对象的类似问题,是否可以同样通过使用接口来解决问题呢?
照例,我们从一个完整的应用实例开始:
//share.cs, Remote Object
namespace Effective.CSharp.Chapter4 {
//Exactly same as the original one
public class RemoteObject : System.MarshalByRefObject {
//a very simple method implementation
public string SayHello(string name) {
return "Hello, " + name;
}
}
}
//server.cs, Server side code
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
namespace Effective.CSharp.Chapter4 {
public class Server {
public static int Main() {
//Register the channel
TcpChannel chan = new TcpChannel(8085);
ChannelServices.RegisterChannel(chan);
//Register the client activated remote class
RemotingConfiguration.ApplicationName = “MyServer”;
RemotingConfiguration.RegisterActivatedServiceType(
typeof(RemoteObject));
//Hold the server, wait for client
System.Console.WriteLine("Hit
1<enter> to exit...");
2
3System.Console.ReadLine();
4
5return 0;
6
7}
8
9}
10
11}
12
13//client.cs, Client Side code
14
15using System;
16
17using System.Runtime.Remoting;
18
19using System.Runtime.Remoting.Channels;
20
21using System.Runtime.Remoting.Channels.Tcp;
22
23namespace Effective.CSharp.Chapter4 {
24
25public class Client
26
27{
28
29public static int Main()
30
31{
32
33TcpChannel chan = new TcpChannel();
34
35ChannelServices.RegisterChannel(chan);
36
37//Register at client side
38
39
40 RemotingConfiguration.RegisterActivatedClientType(
41
42
43 typeof(RemoteObject), "tcp://localhost:8085/MyServer");
44
45//Create the remote object
46
47RemoteObject obj = new RemoteObject();
48
49Console.WriteLine(obj.SayHello("World"));
50
51return 0;
52
53}
54
55}
56
57}
58
59显然,在 CAO 对象的编译和发布方面,我们会碰到与 SAO 对象完全相同的问题。 Client.cs 的编译和运行离不开 share.dll 。 CAO 对象的应用还有几处明显的不同: CAO 对象不仅要求在服务器端进行注册,而且在客户端也需要完成一定的注册工作;更重要的是, CAO 对象是通过 new 创建的!后者彻底阻断了简单的通过接口解决问题的幻想——不可能通过 new 创建出接口的实例,而必须显式的指明要创建的实现特定接口的类型名称!
60
61
62
63很高兴看到你愿意进一步深入了解 .NET Remoting 的具体运作。 J
64
65先来看一下 .NET 内部是如何根据客户请求创建 CAO 对象的。 .NET Remoting 服务会注册一个通用的 Singleton SAO 对象,用于处理所有创建 CAO 对象的请求。这个 SAO 对象实现了 IActivator 接口,其缺省 URI 为 _ <application name=""> _ /RemoteActivationService.rem ,其中 application name 可以通过 RemotingConfiguration. ApplicationName 进行设置。
66
67当客户程序执行 new 语句创建远程对象时,实际上客户端的 .NET Remoting 服务会通过远程对象的 URL 地址连接到服务器端相应的 RemoteActivationService.rem 入口 ( 上面的例子中是 "tcp://localhost:8085/MyServer/RemoteActivationService.rem") ,调用 SAO 对象 IActivator 接口的 Activate 方法; Activate 方法会根据参数查找所有在服务器端注册过的 CAO 对象类型,如果找到相对应的注册信息 ( 包括类型名称、所在的类集以及版本等 ) , Activate 就会根据这些信息创建一个该对象的实例,并且为新创建的对象生成一个唯一的 URI 入口,然后将该对象返回给客户端。这样,此后的客户方法调用就会与这个特定的 CAO 对象实例对应起来。 ( 其中为新生成对象指定 URI 的部分会由底层的 .NET Remoting 服务自动完成, Activate 需要做的仅仅是生成一个新的对象,并且返回该对象的引用 ) 。
68
69可见,远程对象的客户激活机制仅仅是架设在 SAO 基础上的一层包装,在简化程序员工作的同时,这种客户激活机制也剥夺了对 CAO 对象使用接口和抽象基类的权力。既然 .NET 对 CAO 对象的实现没有用到任何特别的方法,我们就完全可以模仿系统实现自己的 CAO 机制,只需少许更改,就可以为子定义的 CAO 提供基于接口的创建方法。
70
71不难看出在整个 CAO 实现体系中, IActivator 实际上是作为一个通用类厂 (Class Factory) 接口出现的,所以我们也需要针对 IRemoteObject 接口定义自己的类厂接口 IRemoteFactory:
72
73//share.cs, Remote Object and Factory interface
74
75namespace Effective.CSharp.Chapter4 {
76
77public interface IRemoteObject {
78
79string SayHello(string name);
80
81}
82
83public interface IRemoteFactory {
84
85IRemoteObject CreateInstance();
86
87}
88
89}
90
91在服务器端的代码中,我们需要实际实现 RemoteObject 和一个用于创建 RemoteObject 的类厂,并且将后者注册为 SAO 对象:
92
93//server.cs, Server side code
94
95using System.Runtime.Remoting;
96
97using System.Runtime.Remoting.Channels;
98
99using System.Runtime.Remoting.Channels.Tcp;
100
101namespace Effective.CSharp.Chapter4 {
102
103public class RemoteObject: MarshalByRefObject,
104
105IRemoteObject {
106
107public string SayHello(string name) {
108
109return “Hello, “ + name;
110
111}
112
113}
114
115public class RemoteFactory: MarshalByRefObject,
116
117IRemoteFactory {
118
119public IRemoteObject CreateInstance() {
120
121return new RemoteObject();
122
123}
124
125}
126
127public class Server {
128
129public static int Main() {
130
131//Register the channel
132
133TcpChannel chan = new TcpChannel(8085);
134
135ChannelServices.RegisterChannel(chan);
136
137//Register the client activated remote class
138
139RemotingConfiguration.RegisterWellKnownServiceType(
140
141typeof(RemoteFactory),
142
143"MyFactory.rem", WellKnownObjectMode.Singleton);
144
145
146
147
148//Hold the server, wait for client
149
150System.Console.WriteLine("Hit <enter> to exit...");
151
152System.Console.ReadLine();
153
154return 0;
155
156}
157
158}
159
160}
161
162最后,客户端的实现代码也略有不同:必须先通过 Activator.GetObject 得到 IRemoteFactory 接口,然后在需要创建 RemoteObject 的时候调用 IRemoteFactory.CreateInstance :
163
164//client.cs, Client Side code
165
166using System;
167
168using System.Runtime.Remoting;
169
170using System.Runtime.Remoting.Channels;
171
172using System.Runtime.Remoting.Channels.Tcp;
173
174namespace Effective.CSharp.Chapter4 {
175
176public class Client
177
178{
179
180public static int Main()
181
182{
183
184TcpChannel chan = new TcpChannel();
185
186ChannelServices.RegisterChannel(chan);
187
188IRemoteFactory factory =
189
190( IRemoteFactory )Activator.GetObject(
191
192
193 typeof(IRemoteFactory), "tcp://localhost:8085/MyFactory");
194
195//Create the remote object
196
197RemoteObject obj = factory.CreateInstance();
198
199Console.WriteLine(obj.SayHello("World"));
200
201return 0;
202
203}
204
205}
206
207}
208
209在以上所有应用接口的实例当中,我们同样可以使用抽象基类代替接口 ( 关于抽象基类和接口的比较,详见条款 X) 。
210
211对于应用 .NET Remoting 技术的分布式应用程序开发,使用接口以及基于接口的类厂是比较好的设计方案,因为这种方案不仅提供了更加清晰的应用模块结构,而且具有更好的可扩展性。
212
213* 本文系原创作品,未经作者本人许可请勿转载。</enter></application></enter>