zoukankan      html  css  js  c++  java
  • 面向对象编程思想-观察者模式

    一、引言

    相信猿友都大大小小经历过一些面试,其中有道经典题目,场景是猫咪叫了一声,老鼠跑了,主人被惊醒(设计有扩展性的可加分)。对于初学者来说,可能一脸懵逼,这啥跟啥啊是,其实博主当年也这感觉,O(∩_∩)O哈哈~好了,废话不多说,今天我们要学习的内容就是要解决这种业务场景——观察者模式,又叫发布-订阅(Publish/Subscrible)模式

    二、观察者模式

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

    下面是观察者模式结构图:

                              该图示出自“大话设计模式”


    下面通过大家都熟悉的生活场景来帮助我们一步步了解观察者模式

    场景:上自习课的时候,困了想睡觉,通常是会跟同桌说:“老师来了叫我下,我睡会”。(大多数人都是这样吧,嘿嘿)

    下面是代码demo:

        //主题类
        class ConcreteSubject
        {
            private IList<ConcreteObserver> lstConcreteObserver = new List<ConcreteObserver>();
            private string action;
            //添加观察者
            public void Add(ConcreteObserver concreteObserver)
            {
                lstConcreteObserver.Add(concreteObserver);
            }
            //移除观察者
            public void Remove(ConcreteObserver concreteObserver)
            {
                lstConcreteObserver.Remove(concreteObserver);
            }
            //通知观察者类
            public void Notify()
            {
                foreach (ConcreteObserver observer in lstConcreteObserver)
                {
                    observer.Update();
                }
            }
            //定义主题发现的某一状态
            public string ConcreteAction
            {
                get { return action; }
                set { action = value; }
            }
        }
        //观察者类
        class ConcreteObserver
        {
            protected ConcreteSubject subject;
            protected string name;
            public ConcreteObserver(ConcreteSubject subject, string name)
            {
                this.subject = subject;
                this.name = name;
            }
            //观察者更新行为
            public void Update()
            {
                Console.WriteLine($"{subject.ConcreteAction},{name},别睡觉了,快醒醒!");
            }
        }
         static void Main(string[] args)
            {
                ConcreteSubject subject = new ConcreteSubject();
                ConcreteObserver observer = new ConcreteObserver(subject, "michael");
                subject.Add(observer);
                subject.ConcreteAction = "老师来了";
                subject.Notify();
                Console.Read();
            }
    View Code

    分析:乍一看是写的不错,实现了老师来时同桌通知michael,但是仔细想一下,假如现在情况变了,同桌另一边的同学在打游戏,也让老师来时通知一下,怎么办?这时我们就需要去修改ConcreteSubject类中对观察者ConcreteObserver的引用,还有另外一种情况,假如某天同桌请假了,通常会让前后排的同学通知观察者,即修改观察者类ConcreteObserver中对ConcreteSubject的引用。这种相互耦合的设计显然是不太好,当场景变了的时候,不得不去修改原有代码,违背了开放-封闭原则。

    下面看一下大话设计模式中的例子是如何介绍观察者模式的:

         //抽象观察者类
        abstract class Observer
        {
           public abstract void Update();
        }
        //抽象主题类
        abstract class Subject
        {
            private IList<Observer> lstConcreteObserver = new List<Observer>();
            public void Add(Observer concreteObserver)
            {
                lstConcreteObserver.Add(concreteObserver);
            }
            public void Remove(Observer concreteObserver)
            {
                lstConcreteObserver.Remove(concreteObserver);
            }
            public void Notify()
            {
                foreach (Observer observer in lstConcreteObserver)
                {
                    observer.Update();
                }
            }
        }
        //具体观察者类
        class ConcreteObserver:Observer
        {
            protected ConcreteSubject subject;
            protected string name;
            private string observerState;
            public ConcreteObserver(ConcreteSubject subject, string name)
            {
                this.subject = subject;
                this.name = name;
            }
            public override void Update()
            {
                this.observerState = subject.ConcreteAction;
                Console.WriteLine($"the observer's of {name} state is {observerState}");
            }
        }
        //具体主题对象
        class ConcreteSubject:Subject
        {      
            private string action;      
            public string ConcreteAction
            {
                get { return action; }
                set { action = value; }
            }
        }
         static void Main(string[] args)
            {
                ConcreteSubject subject = new ConcreteSubject();
                subject.Add(new ConcreteObserver(subject, "michael"));
                subject.Add(new ConcreteObserver(subject, "jarle"));
                subject.Add(new ConcreteObserver(subject, "cumming"));
                subject.ConcreteAction = "Ready";
                subject.Notify();
                Console.Read();
            }
    View Code

     分析:这次是不是可扩展性更高了?嗯,是的。观察者与主题对象都依赖于抽象,而不依赖与具体,使得各自变化都不会影响到另一边

    其实现在已经很好了,但是这样具体的观察者都要依赖于观察者的抽象,那能不能再进行优化一次呢?答案是完全可以的。

    下面我们用委托事件来解决开头那个考烂了的面试题,解释如何优化的

    委托:是一种引用类型,表示对具有特定参数列表和返回类型的方法的引用

        class Mao
        {
            private string name;
            public delegate void MaoDelegateHandler();
            public event MaoDelegateHandler MaoEventHandler;
           
            public Mao(string name)
            {
                this.name = name;
            }
                
            public void Miao()
            {
                Console.WriteLine($"{this.name}叫了一声");
                Notify();
            }
    
            public void Notify()
            {
                if (MaoEventHandler != null)
                    MaoEventHandler();
            }
            public string Name
            {
                get { return name; }
                set { name = value; }
            }
        }
         class Laoshu
        {
            public void Run()
            {
                Console.WriteLine("老鼠逃跑了");
            }
        }
        class People
        {
            public void Wake()
            {
                Console.WriteLine("主人被惊醒了");
            }
        }
        class Program
        {
            static void Main(string[] args)
            {
                Mao mao = new Mao("大脸猫");
                Laoshu laoshu = new Laoshu();
                People people = new People();
                //注册事件 这两种方式都可以
                mao.MaoEventHandler += new Mao.MaoDelegateHandler(laoshu.Run);
                mao.MaoEventHandler += people.Wake;
                //猫叫了一声 会自动调用注册过的方法
                mao.Miao();
                Console.Read();
            }
        }
    View Code

    分析:用委托事件来实现,发布者和订阅者之间没有耦合,是不是有优化了一步呢?O(∩_∩)O~

    优点:

    1.观察者模式解除了发布者和订阅者的耦合,两者都依赖于抽象,而不是具体的,使得两者可以各自独立的变化

    缺点:

    1.观察者对象如果很多的话,被观察者通知会耗时增多

    2.在被观察者之间如果有相互依赖的话,会相互调用,导致系统崩溃(小白注意)

    适用场景:

    1.当一个对象改变需要改变其它对象时,而且不知道有多少个对象需要改变

    2.当一个抽象模型有两个方面,一个方面依赖于另一个方面,将两者封装在独立的对象中以使它们可以各自独立的变化和复用

    介绍到这里其实观察这模式已经结束了,但是我觉得很多小白搞不懂winform开发程序中的grid单击、双击等事件,方法后面的参数sender和e分别是什么。。。下面再简要分析一下

        class Mao
        {
            private string name;
            //声明委托
            public delegate void MaoDelegateHandler(object sender, MaoEventArgs e);
            //声明事件
            public event MaoDelegateHandler MaoEventHandler;
            public class MaoEventArgs : EventArgs
            {
                private string name;
                public MaoEventArgs(string name)
                {
                    this.name = name;
                }
            }
            public Mao(string name)
            {
                this.name = name;
            }
              //发布者 某一行为后 发出通知
            public void Miao()
            {
                Console.WriteLine($"{this.name}叫了一声");
                //建立MaoEventArgs对象
                MaoEventArgs e = new MaoEventArgs(this.Name);
                //调用通知方法
                Notify(e);
            }
    
            public void Notify(MaoEventArgs e)
            {
                //如果有订阅者注册
                if (MaoEventHandler != null)
                    //执行所有注册的方法
                    MaoEventHandler(this,e);
            }
            public string Name
            {
                get { return name; }
                set { name = value; }
            }
        }
         class Laoshu
        {
            //是不是很熟悉,请自行脑补winfom中grid的事件及sender和e是什么
            public void Run(object sender,Mao.MaoEventArgs e)
            {
                Mao mao = (sender as Mao);
                Console.WriteLine($"{mao.Name}来了,老鼠逃跑了");
            }
        }
        class People
        {
            public void Wake(object sender, Mao.MaoEventArgs e)
            {
                Mao mao = (sender as Mao);
                Console.WriteLine($"{mao.Name}的主人被惊醒了");
            }
        }
         class Program
        {
            static void Main(string[] args)
            {
                Mao mao = new Mao("大脸猫");
                Laoshu laoshu = new Laoshu();
                People people = new People();
                //注册事件 这两种方式都可以
                mao.MaoEventHandler += new Mao.MaoDelegateHandler(laoshu.Run);
                mao.MaoEventHandler += people.Wake;
                //猫叫了一声 会自动调用注册过的方法
                mao.Miao();
                Console.Read();
            }
        }
    View Code

    分析:ok,这下面试的时候再也不怕面试官问你观察者模式相关知识了吧。。。

    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。

  • 相关阅读:
    3月21日软件工程概论课堂测验
    四则运算2
    构建之法阅读笔记01
    软件工程学习进度条02-06
    软件工程个人作业01
    简牍《构建之法》
    2月29日课后作业
    读《大道至简》第七八章有感
    个人冲刺——第十天
    人月神话阅读笔记02
  • 原文地址:https://www.cnblogs.com/jdzhang/p/7397544.html
Copyright © 2011-2022 走看看