软件架构师之路--观察者模式

为了便于理解,加深记忆。得举一个最难让人忘记的应用场景,因为之前我也是学了忘,忘了学,当你想不起来观察者模式的时候,通过回忆这个例子,就能很快想起观察者模式的应用

  • 环境

当我们去上课的时候,需要记录老师的电话号码,那么当这位老师变更了他的电话号码,那么需要再上课的时候,老师主动告知同学们,同学们重新记录一遍,老师再变更电话号码,同学们再重新记录一遍,重复以上流程。

  • 应用背景

当某一个对象发生变化的时候,就需要把这种变化通知给其他对象,让其他对象针对这种变化,作出相应调整,在这种场景下就可以使用观察者模式

  • 第一种设计方案

下面的代码--缺点:学生对象里存储个老师的电话号码属性TPhone,老师变更电话号码后,得显式的告知给每一个学生,并没有主动通知

1、Teacher类

namespace Observer
{
    public class Teacher
    {
        private string _phone;

        public string Phone
        {
            get { return _phone; }
            set { _phone = value; }
        }

        public Teacher(string phone)
        {
            this.Phone = phone;
        }
    }
}

2、Student类

namespace Observer
{
    public class Student
    {
        private string _name;
        private string _tPhone;
        
        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }

        public string TPhone
        {
            get { return _tPhone; }
            set { _tPhone = value; }
        }

        public Student(string name)
        {
            this.Name = name;
        }

        public void Show()
        {
            Console.WriteLine("Name:" + this.Name + "\nTeacher Phone:" + this.TPhone);
        }
    }
}

3、调用

namespace Observer
{
    class Program
    {
        static void Main(string[] args)
        {
            Teacher zhangTeacher = new Teacher("123456789");
            Student zhangsan = new Student("张三");
            Student lisi = new Student("李四");
            Student wangwu = new Student("王五");

            zhangsan.TPhone = zhangTeacher.Phone;
            lisi.TPhone = zhangTeacher.Phone;
            wangwu.TPhone = zhangTeacher.Phone;

            zhangsan.Show();
            lisi.Show();
            wangwu.Show();


            //老师的电话号码变更】
            zhangTeacher.Phone = "987654321";

            zhangsan.TPhone = zhangTeacher.Phone;
            lisi.TPhone = zhangTeacher.Phone;
            wangwu.TPhone = zhangTeacher.Phone;

            zhangsan.Show();
            lisi.Show();
            wangwu.Show();

            Console.ReadKey();
        }
    }
}

 

  • 第二种设计方案

下面的代码--缺点:在学生的类里面直接存了一个教师的对象,当一个学生里面存了一个教师的对象之后,他们两个就已经耦合在了一起,无论这个老师教我还是不教我,我是毕业了还是没有毕业,老师的对象永远在我里面存储着,就算我毕业了我不愿意跟这个老师联系了,我们也没有办法删除他,所以说他的扩展性和灵活性仍然是不好的,因为在老师和学生底层直接发生了关联关系

1、Teacher类

namespace Observer
{
    public class Teacher
    {
        private string _phone;

        public string Phone
        {
            get { return _phone; }
            set { _phone = value; }
        }

        public Teacher(string phone)
        {
            this.Phone = phone;
        }
    }
}

2、Student类

namespace Observer
{
    public class Student
    {
        private string _name;
        private Teacher _teacher;

        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }

        public Teacher Teacher
        {
            get { return _teacher; }
            set { _teacher = value; }
        }

        public Student(string name, Teacher teacher)
        {
            this.Name = name;
            this.Teacher = teacher;
        }

        public void Show()
        {
            Console.WriteLine("Name:" + this.Name + "\nTeacher Phone:" + Teacher.Phone);
        }
    }
}

3、调用

namespace Observer
{
    class Program
    {
        static void Main(string[] args)
        {
            Teacher zhangTeacher = new Teacher("张老师");
            Student zhangsan = new Student("张三", zhangTeacher);
            Student lisi = new Student("李四", zhangTeacher);
            Student wangwu = new Student("王五", zhangTeacher);

            zhangsan.Show();
            lisi.Show();
            wangwu.Show();

            //老师的电话号码变更
            Console.WriteLine("============================");
            zhangTeacher.Phone = "987654321";

            zhangsan.Show();
            lisi.Show();
            wangwu.Show();

            Console.ReadKey();
        }
    }
}

  • 观察者模式详解图

 观察者模式中有2个角色,观察者和被观察者

  • 观察者模式实现步骤一

容器--Container

会有一个被观察者等着别人看,看他的一系列变化,还会有一些观察者看着他,注册指的是如果你想要接收到被观察者也就是主题的变化通知的话,你必须得执行注册的操作,在主题对象当中会有1个集合存在,你想要成为他的观察者,就需要把自己注册到这个集合当中,成为观察者列表中的一员

  • 观察者模式实现步骤二

通知--Notify

在主题对象被观察者自身上面,它可能会发生一些状态变化,一但自身状态发生变化,它就会自动的去通知观察者,怎么样去通知?之前已经在被观察者自身存在了1个集合呀,集合里存好了一个一个观察者对象,那它就可以去循环这个集合,把这些观察者一个个拿出来,告诉他们你要针对我的变化发生的响应

  • 观察者模式实现步骤三

 撤销注册--Remove

我们可能一个对象成为了主题对象的观察者以后,观察了一阵,发现我在某种情况下不愿意观察你了,我想不在接收你所有的变化信息,这个时候怎么办呢?这个时候撤销注册,把自己从被观察者里的观察者列表集合里删除出去就好了,所以实现观察者模式最重要的一点就是这个集合的存在

  • 观察者模式设计类图

老师内部会存在一个学生集合,存在这个学生集合以后,它可以往这个集合里面添加观察者对象(具体的学生),所以要有注册观察者方法RegisterObserver(),当观察者不愿意观察以后,它还可以把观察者从队列里移除,所以还要有移除观察者方法RemoveObserver(),最重要当我的状态发生变化以后,我能够通知到我的观察者执行某个方法,所以会有通知观察者方法NotifyObserver(),实际上我们是希望解耦的,我们不希望老师和学生直接发生调用关系,我们希望老师和学生解耦如果不解耦和第一种设计方案以及第二种设计方案是一样的,如何让他们解耦呢?那么就回到了好设计原则中的依赖倒转原则面向抽象进行编程,所以说在现有的老师和学生类之上,我们向上抽取,抽取出来两个接口,一个是观察者接口,观察者接口的作用就是规定如果你想成为观察者,那么你必须要有一个方法Update(),这个方法的目的就是实现让某个通知过来以后你做相应的更新。一个是被观察者接口,作为学生我可以观察老师,还可以观察学院,同样向上抽取,抽取一个被观察者接口,如果你想成为被观察者,你就必须要具有这三种行为
1、要能够往被观察者的观察者列表里注册一个观察者
2、要能够从被观察者的观察者列表里能够把某一个观察者移除
3、还要能够自动的发送变化通知,通知给其他一系列的观察者,让他们完成自动更新的操作

  • 代码具体实现

1、观察者类

namespace ObserverPattern
{
    /// <summary>
    /// 观察者抽象
    /// </summary>
    public interface Obsorver
    {
        void Update(Object obj);
    }
}

2、学生类继承观察者类

namespace ObserverPattern
{
    public class Student:Obsorver
    {
        private string _name;
        private string _tPhone;

        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }

        public string TPhone
        {
            get { return _tPhone; }
            set { _tPhone = value; }
        }

        public Student(string name)
        {
            this.Name = name;
        }

        public void Update(object obj) 
        {
            this.TPhone = (string)obj;
        }

        public void Show()
        {
             Console.WriteLine("Name:" + this.Name + "\nTeacher Phone:" + this.TPhone);
        }
    }
}

3、主题接口,里面有注册观察者,移除观察者,通知观察者三个方法

namespace ObserverPattern
{
    public interface Subject
    {
        /// <summary>
        /// 注册观察者
        /// </summary>
        /// <param name="obj"></param>
        void RegisterObserver(Obsorver obj);

        /// <summary>
        /// 取消注册的观察者
        /// </summary>
        /// <param name="obj"></param>
        void UnRegisterObserver(Obsorver obj);

        /// <summary>
        /// 通知观察者
        /// </summary>
        void NotifyObserver();
    }
}

4、教师类继承自主题接口,实现注册观察者,移除观察者,通知观察者三个方法

namespace ObserverPattern
{
    public class Teacher:Subject
    {
        private ArrayList stuList;//观察者集合列表
        private string _phone;

        public Teacher()
        {
            stuList = new ArrayList();
        }

        public string Phone
        {
            get { return _phone; }
            set
            {
                _phone = value;
                NotifyObserver();
            }
        }

        /// <summary>
        /// 注册观察者
        /// </summary>
        /// <param name="obj"></param>
        public void RegisterObserver(Obsorver obj)
        {
            stuList.Add(obj);
        }

        /// <summary>
        /// 取消注册的观察者
        /// </summary>
        /// <param name="obj"></param>
        public void UnRegisterObserver(Obsorver obj)
        {
            stuList.Remove(obj);
        }

        /// <summary>
        /// 通知观察者
        /// </summary>
        public void NotifyObserver()
        {
            for (int i = 0; i < stuList.Count; i++)
            {
                ((Obsorver)stuList[i]).Update(Phone);
            }
        }
    }
}

5、调用

namespace ObserverPattern
{
    class Program
    {
        static void Main(string[] args)
        {
            Teacher zhangTeacher = new Teacher();
            Student zhangsan = new Student("张三");
            Student lisi = new Student("李四");
            Student wangwu = new Student("王五");


            zhangTeacher.RegisterObserver(zhangsan);
            zhangTeacher.RegisterObserver(lisi);
            zhangTeacher.RegisterObserver(wangwu);

            //老师的电话号码变更
            zhangTeacher.Phone = "123456789";
            //打印学生信息
            zhangsan.Show();
            lisi.Show();
            wangwu.Show();

            //从观察者集合中移除张三同学
            zhangTeacher.UnRegisterObserver(zhangsan);
            Console.WriteLine("============================");
            //老师的电话号码再变更
            zhangTeacher.Phone = "987654321";

            //再打印学生信息
            zhangsan.Show();
            lisi.Show();
            wangwu.Show();

            Console.ReadKey();
        }
    }
}
  • 观察者模式的应用场景

像订阅杂志,订阅天气预报,还有微信公众号中的订阅号

  • .NET框架中观察者模式的应用

在.NET框架中事件就是观察者模式,还有Sql Server如果需要读写分离,读数据库订阅写数据库,那么也是观察者模式
C#中事件是如何来实现的?我们是不是可以通过+=注册事件,-=移除事件,其实事件的本身这种机制就是观察者和被观察者的关系,+=其实就是把自己注册到观察者列表里,-=就是把自己移除出观察者列表

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章