zoukankan      html  css  js  c++  java
  • 设计模式-观察者模式

    定义

    观察者模式(有时又被称为发布-订阅Subscribe>模式、模型-视图View>模式、源-收听者Listener>模式或从属者模式)是软件设计模式的一种。在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实现事件处理系统。 

    基本简介

    观察者模式(Observer)完美的将观察者和被观察的对象分离开。举个例子,用户界面可以作为一个观察者,业务数据是被观察者,用户界面观察业务数据的变化,发现数据变化后,就显示在界面上。面向对象设计的一个原则是:系统中的每个类将重点放在某一个功能上,而不是其他方面。一个对象只做一件事情,并且将他做好。观察者模式在模块之间划定了清晰的界限,提高了应用程序的可维护性和重用性。
    观察者设计模式定义了对象间的一种一对多的依赖关系,以便一个对象的状态发生变化时,所有依赖于它的对象都得到通知并自动刷新。
     

    (1)抽象主题(Subject)

     持有一个观察者对象的集合,提供增加,删除观察者对象的接口,当需要关注的状态变化时候,需要通知所有持有的观察者对象。

    (2)具体主题(Concrete Subject

     被观察者的具体实现...

    (3)抽象观察者(Observer

     定义一个接口,在接受通知时候更新状态

    (4)具体观察者Concrete Observer

     观察者的具体实现...

    UML图

     

    示例

     (1.1)抽象主题类Subject

        /// <summary>
        /// 抽象主题
        /// </summary>
        public abstract class Subject
        {
            /// <summary>
            /// 所有观察者对象
            /// </summary>
            private List<Observer> observers = new List<Observer>();
    
            /// <summary>
            /// 增加观察者对象
            /// </summary>
            /// <param name="observer"></param>
            public void AddObserver(Observer observer)
            {
                observers.Add(observer);
            }
    
            /// <summary>
            /// 移除观察者对象
            /// </summary>
            /// <param name="observer"></param>
            public void RemoveObserver(Observer observer)
            {
                observers.Remove(observer);
            }
    
            /// <summary>
            /// 发送通知
            /// </summary>
            public void Notify()
            {
                foreach (var ob in observers)
                {
                    ob.Update();
                }
            }
        }

    (1.2)具体主题类ConcreteSubject

        /// <summary>
        /// 具体通知者
        /// </summary>
        public class ConcreteSubject : Subject
        {
            /// <summary>
            /// 具体观察者状态
            /// </summary>
            public string SubjectState { get; set; }
        }

    (1.3)抽象观察者类

       /// <summary>
        /// 观察者类
        /// </summary>
        public abstract class Observer
        {
            public abstract void Update();
        }

    (1.4)具体观察者类

        /// <summary>
        /// 具体观察者
        /// </summary>
        public class ConcreteObserver : Observer
        {
            public string observerState { get; set; }
            public string Name { get; set; }
            public ConcreteSubject subjcSubject { get; set; }
    
            public ConcreteObserver(ConcreteSubject subject, string name)
            {
                this.Name = name;
                this.subjcSubject = subject;
            }
    
            public override void Update()
            {
                observerState = subjcSubject.SubjectState;
                System.Console.WriteLine("the observer's state of {0} is {1}", Name, observerState);
            }
        }

    (1.5)控制台调用

        class Program
        {
            static void Main(string[] args)
            {
                ConcreteSubject subject = new ConcreteSubject();
    
                subject.AddObserver(new ConcreteObserver(subject, "ObA"));
                subject.AddObserver(new ConcreteObserver(subject, "ObB"));
                subject.AddObserver(new ConcreteObserver(subject, "ObC"));
    
                subject.SubjectState = "Ready";
                subject.Notify();
    
                System.Console.ReadKey();
            }
        }

    (1.6)运行结果

    优缺点

    优点:
    1、 Subject和Observer之间是松耦合的,分别可以各自独立改变。
    2、 Subject在发送广播通知的时候,无须指定具体的Observer,Observer可以自己决定是否要订阅Subject的通知。
    3、 遵守大部分GRASP原则和常用设计原则,高内聚、低耦合。
    缺陷:
    1、 依赖关系并未完全解除,抽象通知者依旧依赖抽象的观察者。
    2、 如果一个Subject被大量Observer订阅的话,在广播通知的时候可能会有效率问题。
     
    思考:抽象已经降低Subject和Observer之间的耦合度,但是他们之间依旧存在依赖。而我们观察者模式一定程度上注重着对观察者的通知,也就是动作的传递,目前Subject中依靠维持抽象具体观察对象,在发送通知时候循环每个具体观察者,调用通知方法,那么最终我们的目的就是主题能调用相应的通知方法。换个角度说我们是不是可以维护一个方法列表,那么C#中的委托(多播委托)似乎可以实现我们的这一目标。

     

    C#委托改版

    我们以一个主题通知发送消息的例子来演示

    (1.1)抽象主题

        /// <summary>
        /// 抽象主题
        /// </summary>
        public interface ISubject
        {
            void Notify();
        }

    (1.2)声明委托和具体主题

        /// <summary>
        /// 声明委托
        /// </summary>
        public delegate void MsgEvent();
        /// <summary>
        /// 具体主题
        /// </summary>
        public class ConcreteSubject : ISubject
        {
    
            /// <summary>
            /// 定义委托事件
            /// </summary>
            public event MsgEvent MsgAction;
    
            /// <summary>
            /// 执行通知
            /// </summary>
            public void Notify()
            {
                if (MsgAction != null)
                    MsgAction();
            }
        }

    (1.3)添加具体观察者 站内信,邮件,短信

        /// <summary>
        /// 站内信
        /// </summary>
        public class InsideLetterMsg
        {
            /// <summary>
            /// 发送站内信
            /// </summary>
            public void SendInsideLetterMsg()
            {
                Console.WriteLine("发送站内信.....");
            }
        }
    
        /// <summary>
        /// 邮件
        /// </summary>
        public class MailMsg
        {
            /// <summary>
            /// 发送邮件
            /// </summary>
            public void SendMailMsg()
            {
                Console.WriteLine("发送邮件.....");
            }
        }
    
        /// <summary>
        /// 短信
        /// </summary>
        public class SMSMsg
        {
            /// <summary>
            /// 发送短信
            /// </summary>
            public void SendSMSMsg()
            {
                Console.WriteLine("发送短信.....");
            }
        }

    (1.4)客户端调用

        class Program
        {
            static void Main(string[] args)
            {
                ConcreteSubject subject = new ConcreteSubject();
    
                //注册事件
                subject.MsgAction += (new InsideLetterMsg()).SendInsideLetterMsg;  //站内信
                subject.MsgAction += (new MailMsg()).SendMailMsg; //邮件
                subject.MsgAction += (new SMSMsg()).SendSMSMsg;   //短信
    
                //开始发送消息了
                subject.Notify();   
    
                Console.ReadKey();
    
            }
        }

    (1.5)结果

    这个例子我们更多关注的是行为,主题对于观察者行为的执行和通知。我们只需要在调用的时候将观察者的方法注册到主题中即可。省去了主题需要维护观察者对象,循环调用观察者对象方法的过程。顺带我们也看一下我们创建的委托具体是what?

    委托看一看

    (1.1)看图说话

    (1.2)System.MulticastDelegate

      

       _invocationList通常这个字段为null,当我们构造一个委托链是,他可以引用一个委托数组,也就是说我们给委托+=方法时候,实际是操作它.

       我们可以推断出当我们调用委托方法时候,代码大致是这样的(下面代码不是可运行的代码,只是预估大概的逻辑,仅供观看),不知道在观察者这块顺带写了下委托是不是有点

       跑偏,委托这里只是顺带提一下,更多的知识面肯定没涉及到,只是帮助大家理解下.

                //从委托链中获取
                Delegate[] deleagetset = _invocationList as Delegate[];
                if (deleagetset != null)
                {
                    foreach (MsgAction m in deleagetset)
                    {
                        m(value); //调用每个委托
                    }
                }
                else 
                {
                    //当前不是委托链 直接 invoke
                    _methodPtr.Invoke(_target, value);
                }

     上边的例子只是单纯的循环,中途有一个调用委托失败都没有健壮的处理,所以MulticastDelegate类提供了另一个实例方法GetInvocationList,用于显示调用链中的每一个委托,具体大家可以查阅相关资料。That's all!

    文章代码链接http://files.cnblogs.com/files/mongo/BlogObserver.zip

     

  • 相关阅读:
    SQLite的总结与在C#的使用
    Linq中比较字符串类型的日期
    C#中委托,匿名函数,lamda表达式复习
    MYSQL中SUM (IF())
    C#在属性中用Lambda语法
    Mysql绿色版安装和遇到的问题
    FormsAuthentication权限管理
    存储过程中高性能安全式SQL拼接
    JavaScript实现搜索联想功能
    JavaScript组成
  • 原文地址:https://www.cnblogs.com/mongo/p/4511216.html
Copyright © 2011-2022 走看看