设计模式之观察者模式
一、概论
什么是观察者模式呢?其实也叫订阅者模式,如果多个用户订阅了这个主题,在这个主题发生改变的时候,每一个用户都能够接受到这个主题推送的通知,观察者模式主要分两种角色,一种是Subject(主题类),另一种是Observer(监视类),主题类为监听类提供了两个方法,一个是Register ,Observer通过这个方法来申明监听或者说订阅了这个主题,一个个方法是UnRegister ,是用来移除监听对象,但Subject 类发生改变的时候,调用Notify方法,让后每个订阅者都能够收到通知,就像读者订阅了报纸,当报纸出新的版本事,就可以通知读者这个报纸出新版本了,各位读者就是订阅者,报纸就是主题类。
二、定义接口
根据.net的编码规范,我们将监视类的接口定义为IObserver,主题类的接口定义为IObservable,表示的是被监听的意思,
IObserver中定义了Update方法
/// <summary> /// 定义观察者接口 /// </summary> public interface IObserver { void Update(); }
IObservable中定义了两个方法 Register 和UnRegister
/// <summary> /// 定义被观察者接口 /// </summary> public interface IObservable { /// <summary> /// 注册 /// </summary> /// <param name="observer"></param> void Register(IObserver observer); /// <summary> /// 取消注册 /// </summary> /// <param name="observer"></param> void UnRegister(IObserver observer); }
三、观察者模式实现
现在定义一个报纸的类,它继承与接口IObserver
/// <summary> /// 被观察具体实现类(报纸) /// </summary> public class NewsPaper : IObservable { List<IObserver> objs = new List<IObserver>(); public void Register(IObserver observer) { objs.Add(observer); } public void UnRegister(IObserver observer) { objs.Remove(observer); } public void Notify() { if (objs!=null&objs.Count>0) { foreach (var obj in objs) { obj.Update(); } } } }
在这个类中,我们定义了一个裝取监视着的一个容器,Register的方法就是将注册的监视着放进这个容器,UnRegister就是移除监视者,在调用Notify()方法时遍历容器中的所有的监视者,通知所有的监视者。
下面我们定义了连个读者的类
public class Wang : IObserver { public void Update() { Console.WriteLine("你好Wang,你订阅的报纸已经发版"); } }
public class Li : IObserver { public void Update() { Console.WriteLine("你好Li,你订阅的报纸已经发版"); } }
接下来我们运行下面的程序
class Program { static void Main(string[] args) { Wang p1 = new Wang(); Li p2 = new Li(); NewsPaper log = new NewsPaper(); log.Register(p1); log.Register(p2); log.Notify(); log.UnRegister(p2); log.Notify(); Console.ReadKey(); } }
运行结果如下:
这样我们就简单的实现了观察者模式
四、推模式
推模式是什么呢,推模式就是把订阅者需要的信息推送给订阅者,如何推?就是将需要的信息封装到一个类中通过Notify方法推送给Observer
我们先定义一个推送的类NewsEventArgs ,这个类是用来储存报纸的价格的。
public class NewsEventArgs { private int price; public int Price { get { return price; } } public NewsEventArgs(int price) { this.price = price; } }
NewsPaper类中的Notify方法要发生改变,代码如下
/// <summary> /// 被观察具体实现类 /// </summary> public class Log : IObservable { List<IObserver> objs = new List<IObserver>(); public void Register(IObserver observer) { objs.Add(observer); } public void UnRegister(IObserver observer) { objs.Remove(observer); } public void Notify() { int price=100; if (objs != null & objs.Count > 0) { foreach (var obj in objs) { var args = new NewsEventArgs(price); obj.Update(args); price++; } } } }
在订阅者的类中的Update方法也相应发生些改变
/// <summary> /// 观察者具体实现 /// </summary> public class People1 : IObserver { public void Update(NewsEventArgs args) { Console.WriteLine("你好,你订阅的报纸已经发版,价格是 " + args.Price); } }
运行结构如下
五、拉模式
推模式就是主题者作为参数将自身返回给订阅者,所有订阅者获取到的信息是一样的,这个可以用来传递主题类的自身描述。
下面是修改后的newspaper类
/// <summary> /// 被观察具体实现类 /// </summary> public class NewsPaper : IObservable { public string Type { get { return "经济"; } } public DateTime SubTime{ get { return DateTime.Now; } } List<IObserver> objs = new List<IObserver>(); public void Register(IObserver observer) { objs.Add(observer); } public void UnRegister(IObserver observer) { objs.Remove(observer); } public void Notify() { if (objs != null & objs.Count > 0) { foreach (var obj in objs) { obj.Update(this); } } } }
下面是修改后的读者的类
/// <summary> /// 观察者具体实现 /// </summary> public class Wang : IObserver { public void Update(IObservable sender) { NewsPaper news= (NewsPaper)sender; Console.WriteLine("你好 wang,你订阅的报纸已经发版,发布时间:" + news.SubTime + "类型是" + news.Type); } }
运行结果
六、推模式和拉模式的区别
推模式和拉模式到底有什么区别,我认为是这样的,
推模式是订阅者需要什么我们就给什么,数据是针对某一个用户,具有隐私性,就像价格,虽然A,B都订阅了报纸,但他们的价格可能是不同的,这种情况就适合用推模式
拉模式是不管订阅者是什么,我们都将相同的东西返回给订阅者,这个可以是主题者自身的信息。
当然我们也可以两种模式一起使用,在.net中,事件委托就是同时用了两种模式
在实际运用中我们要根据实际情况来运用观察者模式