用于Web Service调用的Subject-Observer关系
设计
1、 限制Subject.State:由于Web Service调用中,消息需要被XML序列化,然后保存在SOAP消息中,因此不是什么类型都可以用于这个调用的,必须为他“贴”上一个SerializableAttribute的标签或者验证它的类型是否Type.IsSerializable?;
2、 将Subject的Attach、Detach暴露为WSDL接口,同时Observer的Update也暴露为WSDL接口;
3、 在Subject服务端增加一个指向各个Observer的Stub类,用于调用他们的Update方法,同时在涉及到会更新Subject内容的Observer端增加一个透明的Proxy类,用户修改Subject.State;(一般情况下,这些工作可以通过开发工具自动完成);
修改后的结构如下(为了简化,同时考虑到System.Web.Services.WebService已经占用了基类的位置,因此把两个抽象类去掉):

1、定义Web Service版本Subject和Observer接口
说明:
•与经典方式下进程内的Subject-Observer不同,由于调用会通过无状态的Web Service完成,因此只能使用方法而不能使用属性Property。
•由于抽象的IWebServiceObserver接口为引用对象,无法通过调用常规的Attach方法和Detach方法进行注册,需要借助Stub间接完成,为了保证所有的参数都可以被序列化,这里为每个Observer对象增加了逻辑名称,在Web Service接口层面Attach和Detach仅针对逻辑名称操作,而并非直接使用具体Observer的引用实例。
•所有的泛型参数<T>必须为可以序列化的类型。
///C# IWebServiceSubject<T> using System.Collections.Generic; namespace VisionTask.Training.ServicePattern.UtilityService { /// <summary> /// 抽象Web Service Subject接口 /// </summary> /// <remarks> /// 为了让IWebServiceSubject仅仅依赖于抽象的IWebServiceObserver, /// 但又确保实体WebServiceObserver可以被注册,采用变通的方法, /// WebServiceSubject中仅仅登记每个WebServiceObserver的名称,具体调度 /// 哪个WebServiceObserver实体则是通过访问Stub获得。 /// </remarks> public interface IWebServiceSubject<T> { string[] GetObserverNames(); void Attach(string observerName); void Detach(string observerName); void SetState(T state); T GetState(); void Notify(); } } ///C# IWebServiceObserver<T> using System; using System.Runtime.Serialization; namespace VisionTask.Training.ServicePattern.UtilityService { /// <summary> /// 抽象Web Service Observer接口 /// </summary> public interface IWebServiceObserver<T> { /// <summary> /// 每个Observer对象的逻辑名称 /// </summary> string Name { get;} T GetData(); void Update(T data); } }
2、为所有Observer Web Service准备抽象基类
///C# ObserverServiceBase<T> using System; using System.Collections.Generic; using System.Web.Services; using System.Runtime.Serialization; namespace VisionTask.Training.ServicePattern.UtilityService { public abstract class ObserverServiceBase<T> : WebService, IWebServiceObserver<T> { static ObserverServiceBase() { // 判断提供的泛型参数是否可以序列化 if (!typeof(T).IsSerializable) throw new InvalidOperationException(); } public abstract void Update(T data); public abstract T GetData(); public abstract string Name { get;} } }
3、实现两个Observer Web Service
///C# DoubleDataObserverService using System; using System.Web.Services; namespace VisionTask.Training.ServicePattern.UtilityService { [Serializable] [WebService] public class DoubleDataObserverService : ObserverServiceBase<int> { private static int current = 0; [WebMethod] public override void Update(int data) { current = data; } [WebMethod] public override int GetData() { return current * 2; } public override string Name { get { return "DoubleData"; } } } } ///C# PlusOneObserverService using System; using System.Web.Services; namespace VisionTask.Training.ServicePattern.UtilityService { [Serializable] [WebService] public class PlusOneObserverService : ObserverServiceBase<int> { private static int current; [WebMethod] public override void Update(int data) { current = data; } [WebMethod] public override int GetData() { return current + 1; } public override string Name { get { return "PlusOne"; } } } }
4、定义Subject Web Service 的Stub类型
说明:
•通过在Subject Web Service项目中引用每个Observer Web Service,由开发工具自动生成对应的Observer Web Service Proxy类;(实际项目中也可以手工生成Proxy类)
•Stub实际登记各个Observer Web Service的Proxy类型,允许Subject Web Service按照Observer的名称访问具体Proxy类的实例;
•为了便于Subject在通知过程中遍历每个Observer Web Service的Proxy实例,为Stub增加了迭代器;
///C# Stub #region Using using System; using System.Collections.Generic; using VisionTask.Training.ServicePattern.UtilityService.DoubleDataObserver; using VisionTask.Training.ServicePattern.UtilityService.PlusOneObserver; #endregion namespace VisionTask.Training.ServicePattern.UtilityService { /// <summary> /// Subject Web Service 对象用于访问其他Observer Web Service的Stub类 /// </summary> internal class SubjectStub { private static IDictionary<string, IWebServiceObserver<int>> observers; /// <summary> /// 初始化过程加载每个Observer的Proxy信息 /// </summary> static SubjectStub() { observers = new Dictionary<string, IWebServiceObserver<int>>(); IWebServiceObserver<int> observer = new DoubleDataObserverService(); observers.Add(observer.Name, observer); observer = new PlusOneObserverService(); observers.Add(observer.Name, observer); } /// <summary> /// 根据Observer的名称,提出相关Observer Web Service的Proxy实例; /// </summary> /// <param name="name">Observer的逻辑名称</param> public IWebServiceObserver<int> this[string name] { get { if(string.IsNullOrEmpty(name)) throw new ArgumentNullException("name"); IWebServiceObserver<int> observer; if(observers.TryGetValue(name, out observer)) return observer; else return null; } } /// <summary> /// 为了便于Subject Web Service,Stub采用Iterator模式对外提供每个注册 /// 好的Observer Web Server Proxy的引用。 /// </summary> /// <returns></returns> public IEnumerable<IWebServiceObserver<int>> GetObservers() { foreach (IWebServiceObserver<int> observer in observers.Values) yield return observer; } } }
5、定义Subject Web Service
///C# using System; using System.Collections.Generic; using System.Web.Services; namespace VisionTask.Training.ServicePattern.UtilityService { [WebService] public class SubjectService : WebService, IWebServiceSubject<int> { /// <summary> /// 登记所有已经被Stub注册的Observer Web Service逻辑名称. /// </summary> private static IList<string> observerNames = new List<string>(); /// <summary> /// Subject Web Service保存的当前信息 /// </summary> private static int current = 0; /// <summary> /// 获取所有已经登记的Observer Web Service逻辑名称 /// </summary> [WebMethod] public string[] GetObserverNames() { if(observerNames.Count == 0) return null; string[] result = new string[observerNames.Count]; observerNames.CopyTo(result, 0); return result; } [WebMethod] public void Attach(string observerName) { if (string.IsNullOrEmpty(observerName) || (observerNames.Contains(observerName))) return; observerNames.Add(observerName); } [WebMethod] public void Detach(string observerName) { if (string.IsNullOrEmpty(observerName) || (!observerNames.Contains(observerName))) return; observerNames.Remove(observerName); } [WebMethod] public void SetState(int state) { current = state; Notify(); } [WebMethod] public int GetState() { return current; } /// <summary> /// 该方法在Web Service端自动执行,不需要Web Service Client显式调用, /// 所以不用声明为[WebMethod] /// </summary> public void Notify() { foreach (IWebServiceObserver<int> observer in (new SubjectStub()).GetObservers()) observer.Update(current); } } }
6、单元测试验证
///C# UnitTest ///首先,将上述3个Web Service在单元测试项目中引用,分别命名为: DoubleDataObserverService PlusOneObserverService SubjectService using System; using Microsoft.VisualStudio.TestTools.UnitTesting; using VisionTask.Training.ServicePattern.UtilityService; namespace VisionTask.Training.ServicePattern.UtilityService.UnitTest { [TestClass] public class ServiceObserverTest { [TestMethod] public void Test() { // 创建对个Web Service的本地实例 // 1个Subject Web service 和2个Observer Web service。 SubjectService.SubjectService subject = new SubjectService.SubjectService(); PlusOneObserverService.PlusOneObserverService plusOneObserver = new PlusOneObserverService.PlusOneObserverService(); DoubleDataObserverService.DoubleDataObserverService doubleDataObserver = new DoubleDataObserverService.DoubleDataObserverService(); // 注册各Observer subject.Attach("PlusOne"); subject.Attach("DoubleData"); // 更新Subject信息 subject.SetState(10); Assert.AreEqual<int>(10, subject.GetState()); // 检查Subject对各个Observer的通知情况 Assert.AreEqual<int>(11, plusOneObserver.GetData()); Assert.AreEqual<int>(20, doubleDataObserver.GetData()); } } }