c#之观察者模式
观察者模式
前言:最近花心思学习了一下设计模式,总体感觉设计模式主要是给我们提供解决问题的一种思路,是自己的代码可重用性高,保证代码的可靠性,设计模式使代码编制真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。
首先我们通过一个故事来进行探讨:在曾经的一段时间里,我们的股市是非常火爆的,几乎接近了全名炒股的程度,张三(化名)经常会在上班的时候关注股市的信息,但总是会担心老板出现在自己的身后,这样总归是不太好的。在这个时候,张三想到了一个办法,在它的公司有一个前台的秘书,平常没事的时候经常找她聊聊天,关系也是挺不错的。这时候他就请前台的秘书(小童)请她帮忙,如果老板的时候让她提前打个招呼。这样就不会怕发现了。
大家通过上面的故事:这个事情就是一个典型的观察者模式。
定义:
观察者模式:定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象,这个主题对象在状态发生改变的时候,会通知所有的观察者对象,使他们能够自动的进行更新。
我们先给出图形方便大家的理解,如下:
我们首先定义抽象通知类接口,通过我们的开放-封闭原则和依赖倒转原则,我们应该让程序都依赖于抽象,而不是相互依赖。我们选择了定义抽象通知类接口,代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 观察者模式 { /// <summary> /// 抽象通知者接口 /// </summary> interface Subject { /// <summary> /// 增加监听 /// </summary> /// <param name="observer"></param> void Attach(Observer observer); /// <summary> /// 删除监听 /// </summary> /// <param name="observer"></param> void Detach(Observer observer); /// <summary> /// 发送通知 /// </summary> void Notify(); /// <summary> /// 前台的状态 /// </summary> string SubjectState { get; set; } } }
那么接下来该定义抽象通知者的实现类了,我们继承的接口必须要实现里面全部的方法。全部代码的实现如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 观察者模式 { /// <summary> /// 前台通知者类 /// </summary> class FrontDesk : Subject { //同事列表 private IList<Observer> observers = new List<Observer>(); //行动 private string action; /// <summary> /// 前台通过电话 所说的话或者是所做的事情 /// </summary> public string SubjectState { get { return action; } set { action = value; } } /// <summary> /// 增加观察者 /// </summary> /// <param name="observer">观察者</param> public void Attach(Observer observer) { observers.Add(observer); } /// <summary> /// 减少观察者 /// </summary> /// <param name="observer">观察者</param> public void Detach(Observer observer) { observers.Remove(observer); } /// <summary> /// 发出通知 /// </summary> public void Notify() { foreach (var o in observers) { o.Update(); } } } }
好的我们的抽象的通知者已经定义好了,接下来我们定义抽象的观察者,我们各个的观察者都需要一个更新自身的状态的方法.代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 观察者模式 { /// <summary> /// 抽象观察者 /// </summary> abstract class Observer { //员工名字 protected string name; //通知者的定义 protected Subject sub; /// <summary> /// 构造函数进行初始化赋值操作 /// </summary> /// <param name="name">名字</param> /// <param name="sub">通知者</param> public Observer(string name, Subject sub) { this.name = name; this.sub = sub; } /// <summary> /// 更新自身的状态 /// </summary> public abstract void Update(); } }
接下来我们定义看股票观察者看NBA观察者并分别继承抽象观察者类,详细代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 观察者模式 { /// <summary> /// 实体类:看股票的同事 /// </summary> class StockObserver : Observer { /// <summary> /// 继承父类的构造函数 /// </summary> /// <param name="name">名字</param> /// <param name="sub">通知者</param> public StockObserver(string name, Subject sub) : base(name, sub) { } /// <summary> /// 自身的状态的改变 /// </summary> public override void Update() { Console.WriteLine("{0},{1},关闭股票行情,继续工作", sub.SubjectState, name); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 观察者模式 { class LookNBAObserver : Observer { /// <summary> /// 继承的构造函数 /// </summary> /// <param name="name"></param> /// <param name="sub"></param> public LookNBAObserver(string name, Subject sub) : base(name, sub) { } /// <summary> /// 改变自身的状态 /// </summary> public override void Update() { Console.WriteLine("{0},{1},关闭NBA,继续工作", sub.SubjectState, name); } } }
前台的调用的代码,详细代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 观察者模式 { class Program { static void Main(string[] args) { //前台花花 FrontDesk frontDesk = new FrontDesk(); //看屁股(哈哈哈)的同事 StockObserver stockObserver = new StockObserver("张雷", frontDesk); //看NBA的同事 LookNBAObserver lookNbaObserver = new LookNBAObserver("骚鹏", frontDesk); //增加观察者 frontDesk.Attach(stockObserver); frontDesk.Attach(lookNbaObserver); //花花发送消息,老板回来了 frontDesk.SubjectState = "老板回来了"; //减少观察者,这时候我不想通知张雷了,等着被老板抓吧,哈哈。 frontDesk.Detach(stockObserver); //发出消息 frontDesk.Notify(); Console.ReadKey(); } } }
截图显示:
观察者模式的特点
我们接下来需要思考一个问题:用观察者模式的动机的什么呢?
将一个系统分割成一系列相互协作的类有一个很不好的副作用,那就是需要维护相关对象间的一致性。我们不希望为了维护一致性而是各类紧密耦合,这样会给维护,扩展和重用带来不便。
我们什么使用使用观察者模式呢:当一个对象的改变需要同时的改变其他的对象的时候。
其实当我们的通知者发出消息的时候,并不知道谁是他的观察者,也就是说,具体观察者是谁,他根本不需要知道是谁。而任何一个观察者也不知道也不需要知道其他的观察者的存在。
总体来说:观察者所做的工作其实是在解除耦合,让耦合的双方都依赖于抽象,而不是依赖于具体,从而使得各自的变化都不影响到另一边的变化。
观察者模式的不足
尽管我们用了依赖倒转原则,但是抽象通知者还是依赖于抽象观察者,也就是说,万一没有了抽象观察者这样的接口的实现,这边的功能就实现不了了。
我们接下来的改造:我们只需要在通知者中广播一个方法,至于是谁关注他不管,如果观察者对这条信息感兴趣就关注,不感兴趣就不关注。
事件委托实现观察者模式(推荐使用)
定义抽象通知者接口,代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 委托与事件的方式实现观察者模式 { interface Subject { //发送通知 void Notify(); //前台的状态 string SubjectState { get; set; } } }
定义前台类实现接口,代码如下:
using System; using System.Collections.Generic; using System.Diagnostics.Eventing.Reader; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 委托与事件的方式实现观察者模式 { //委托的声明 delegate void EventHandler(); /// <summary> /// 前台通知者 /// </summary> class FrontDesk : Subject { //事件的声明 public static event EventHandler Update; /// <summary> /// 前台的状态 所说的话或者所做的事情 /// </summary> private string _subjectState; public string SubjectState { get { return _subjectState; } set { _subjectState = value; } } /// <summary> /// 发送消息 /// </summary> public void Notify() { Update(); } } }
定义抽象观察者,代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 委托与事件的方式实现观察者模式 { abstract class Observer { //名字 protected string name; //对通知者的引用 protected Subject sub; /// <summary> /// 构造函数 /// </summary> /// <param name="name">名字</param> /// <param name="sub">通知者</param> public Observer(string name, Subject sub) { this.name = name; this.sub = sub; } /// <summary> /// 更新自身的状态 /// </summary> public abstract void Update(); } }
观察者的实现类,代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 委托与事件的方式实现观察者模式 { class StockObserver : Observer { public StockObserver(string name, Subject sub) : base(name, sub) { //感兴趣进行关注,进行事件的注册 FrontDesk.Update += Update; } /// <summary> /// 重写父类的方法,更新自己行为的方法 /// </summary> public override void Update() { Console.WriteLine("{0} {1},关闭股票行情,继续工作", sub.SubjectState, name); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 委托与事件的方式实现观察者模式 { class NBAObserver : Observer { public NBAObserver(string name, Subject sub) : base(name, sub) { //事件的注册 FrontDesk.Update += Update; } public override void Update() { Console.WriteLine("{0} {1},关闭NBA直播,继续工作", sub.SubjectState, name); } } }
客户端代码的调用,代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 委托与事件的方式实现观察者模式 { class Program { static void Main(string[] args) { //前台花花 FrontDesk huahua = new FrontDesk(); //看股票的同事 new StockObserver("张三", huahua); //看NBA直播的同事 new NBAObserver("李四", huahua); huahua.SubjectState = "老板来了"; //消息的发送 huahua.Notify(); Console.ReadKey(); } } }
调试结果截图如下:
这样的话委托和事件的方式很好的实现了观察者模式!!好的我们的观察者模式就讲解完了,大家有什么问题和建议请大家积极的发言,明天就是10.24,程序员节快乐。