• 【笔记】大话设计模式14-观察者模式


    【笔记】大话设计模式14-观察者模式

    14 观察者模式

    14.1 Example

    最近加班越来越多,阿三感到非常疲惫,于是上班的时候想摸摸鱼。于是趁老板出去的时候,打开手机,看看股票是否跌破3000点了,看看基金有没有扳回来一点。但是万一老板回来了怎么办?还好他人脉广,认识前台的大头,跟大头打个招呼,老板一回来,就发个信息给他

    这就是典型的观察者模式,又叫发布-订阅(Publish/Subscribe)模式。观其其他事务的状态,根据其状态改变做出对应的策略。

    14.2 定义

    ::: block-1

    观察者模式:定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己
    :::

    14.3 Show me the code

    观察者与通知者均使用抽象类,方便通知者和观察者的扩展与个例的实现,观察不同对象,通知者也有不同的通知形式。

    14.3.1 通知者

    Subject类

    可翻译为主题或抽象通知者,一般用一个抽象类或者一个接口实现,可以添加或者移除观察者,并通知

        abstract class Subject
        {
            private IList observers = new List();
    
            //增加观察者
            public void Attach(Observer observer)
            {
                observers.Add(observer);
            }
            //移除观察者
            public void Detach(Observer observer)
            {
                observers.Remove(observer);
            }
            //通知
            public void Notify()
            {
                foreach (Observer o in observers)
                {
                    o.Update();
                }
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    ConcreteSubject类

    具体的通知者,将有关状态存入具体的观察者对象,在具体通知者内部状态改变时,给所有登记过的观察者发出通知,就是通知者发现老板来了,要赶紧通知正在摸鱼的同事们。

    //具体通知者
    class ConcreteSubject : Subject
    {
        private string subjectState;
    
        //具体通知者状态
        public string SubjectState
        {
            get { return subjectState; }
            set { subjectState = value; }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    14.3.2 观察者

    Observer类

    抽象观察者,在得到通知者的通知时,更新自己,这个接口叫更新接口,通常包含一个Update方法,这个方法叫更新方法

    abstract class Observer
    {
        public abstract void Update();
    }
    
    • 1
    • 2
    • 3
    • 4
    ConcreteObserver类

    具体观察者。具体观察者角色可以保存一个指向具体通知者对象的引用

       class ConcreteObserver : Observer
        {
            private string name;
            private string observerState;
            private ConcreteSubject subject;
    
            public ConcreteObserver(ConcreteSubject subject, string name)
            {
                this.subject = subject;
                this.name = name;
            }
            //更新
            public override void Update()
            {
                observerState = subject.SubjectState;
                Console.WriteLine("观察者{0}的新状态是{1}",
                  name, observerState);
            }
    
            public ConcreteSubject Subject
            {
                get { return subject; }
                set { subject = value; }
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    14.3.3 客户端

        class Program
        {
            static void Main(string[] args)
            {
                ConcreteSubject s = new ConcreteSubject();
    
                s.Attach(new ConcreteObserver(s, "X"));
                s.Attach(new ConcreteObserver(s, "Y"));
                s.Attach(new ConcreteObserver(s, "Z"));
    
                s.SubjectState = "ABC";
                s.Notify();
    
                Console.Read();
    
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    结果:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uB4Wlz0a-1658836565459)(https://files.mdnice.com/user/27157/3226af16-1c39-44b0-b5de-2faf87d132c3.png)]

    14.4 总结

    使用场景:

    • 当一个对象的改变需要同时改变其他对象的时候,并且不知道具体有多少对象需要改变;
    • 一个抽象模型有2个方面,一方面依赖于另一方面,可以将两者封装在独立的对象中,使他们各自独立地改变和复用
    • 观察者模式就是解除耦合,耦合的双方都依赖于抽象而非具体。

    不足:

    独立的观察者类都需要实现一个Observer的接口,而有些独立的类之间并没有观察者接口,如果没有,通知就没法下发了。

    应该由客户端决定,谁改变了通知谁。

    事件委托

    ::: block-1

    委托就是一种引用方法的类型。

    一旦为委托分配了方法,委托将与该方法具有完全相同的行为

    委托可以有参数和返回值,可以看成是对函数的抽象,是函数的“类”,委托的实例代表一个具体的函数。

    一个委托可以搭载多个方法,所有方法被依次唤起。方法的入参和返回值要与委托的定义相同
    :::
    看具体代码:

    using System;
    using System.Collections.Generic;
    using System.Text;
    
    namespace 观察者模式
    {
        class Program
        {
            static void Main(string[] args)
            {
                //老板Randy
                Boss huhansan = new Boss();
    
                //看股票的同事
                StockObserver tongshi1 = new StockObserver("阿三", huhansan);
                //看NBA的同事
                NBAObserver tongshi2 = new NBAObserver("大头", huhansan);
    			/// 将不同的方法搭载到老板的更新上,从而通知观察者做各自的更新,
                // 观察者不需要统一的Observer接口
                huhansan.Update += new EventHandler(tongshi1.CloseStockMarket);
                huhansan.Update += new EventHandler(tongshi2.CloseNBADirectSeeding);
    
                //老板回来
                huhansan.SubjectState = "我Randy回来了!";
                //发出通知
                huhansan.Notify();
    
                Console.Read();
    
    
            }
        }
    
        //通知者接口
        interface Subject
        {
            void Notify();
            string SubjectState
            {
                get;
                set;
            }
        }
    
        //事件处理程序的委托
        delegate void EventHandler();
    
        class Secretary : Subject
        {
            //声明一事件Update,类型为委托EventHandler
            public event EventHandler Update;
    
            private string action;
    
            public void Notify()
            {
                Update();
            }
            public string SubjectState
            {
                get { return action; }
                set { action = value; }
            }
        }
    
        class Boss : Subject
        {
            //声明一事件Update,类型为委托EventHandler
            public event EventHandler Update;
    
            private string action;
    
            public void Notify()
            {
                Update();
            }
            public string SubjectState
            {
                get { return action; }
                set { action = value; }
            }
        }
    
        //看股票的同事
        class StockObserver
        {
            private string name;
            private Subject sub;
            public StockObserver(string name, Subject sub)
            {
                this.name = name;
                this.sub = sub;
            }
    
            //关闭股票行情
            public void CloseStockMarket()
            {
                Console.WriteLine("{0} {1} 关闭股票行情,继续工作!", sub.SubjectState, name);
            }
        }
    
        //看NBA的同事
        class NBAObserver
        {
            private string name;
            private Subject sub;
            public NBAObserver(string name, Subject sub)
            {
                this.name = name;
                this.sub = sub;
            }
    
            //关闭NBA直播
            public void CloseNBADirectSeeding()
            {
                Console.WriteLine("{0} {1} 关闭NBA直播,继续工作!", sub.SubjectState, name);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119

    运行结果:

  • 相关阅读:
    QLabel中显示版权符号©
    带声学释放器的近海海底潜标的回收记录
    .net SDK 版本信息查询
    【附源码】Python计算机毕业设计农业技术学习平台
    nohup命令后台启动jar包
    每日一练 | 华为认证真题练习Day121
    使用 Socks5 来劫持 HTTPS(TCP-TLS) 之旅
    西宾得到语音下载工具(dedaodown
    Django-中间件(切面编程AOP)
    书本整理
  • 原文地址:https://blog.csdn.net/moneymyone/article/details/126002168