阅读 26

观察者模式

定义

观察者定义了对象之间的一对多关系,当一个对象改变时,它的所有依赖者都会收到通知并自动更新。

案例意图

  • 一般主题只有一个,而观察者可以有多个。
  • 观察者可以随时取消与主题的关系,而其它观察者并不受影响,主题也不受影响

算法优点

  • 观察者和主题之间有很好的松耦合性,主题可以随时取消与观察者的联系,其他观察者并不会收到影响。
  • 不需要知道观察者是怎样发生变化的,只需要推送信息即可。

算法缺点

  • 若观察者太多,每个都通知则会浪费大量的时间。
  • 若观察者之间存在循环调用,则会造成崩溃。

实现方式

  • 定义某个抽象,让观察者继承或实现该抽象,主题存有观察者列表(列表中的对象都继承或实现同一抽象),当主题发生变化时,挨个通知观察者。
  • C#的委托

代码实现

  • 方式一
namespace Algorithm.Design
{
    class ObserverMode
    {
        public static void Main()
        {
            SubjectInterface subject = new Subject(); //主题
            ObserverInterface one = new ObserverOne(subject); //观察者
            ObserverInterface two = new ObserverTwo(subject);
            Run(subject);
            two.Remove();
            Console.WriteLine("-==========================");
            Run(subject);
        }

        public static void Run(SubjectInterface subject)
        {
            for (int i = 0; i < 100; i++)
            {
                if (i == 44)
                {
                    subject.NotifyObserver(i);//通知观察者
                    break;
                }
            }
        }
    }

    public interface ObserverInterface
    {
        void Update(int time);
        void Remove();
    }

    public interface SubjectInterface
    {
        void AddObserver(ObserverInterface o);
        void RemoveObserver(ObserverInterface o);
        void NotifyObserver(int time);
    }

    public class Subject : SubjectInterface
    {
        List<ObserverInterface> observerList = new List<ObserverInterface>(); //观察者列表

        public Subject() { }

        public void AddObserver(ObserverInterface o) //加入观察者
        {
            observerList.Add(o);
        }

        public void NotifyObserver(int time) //通知
        {
            foreach(ObserverInterface o in observerList)
            {
                if(o != null)
                {
                    o.Update(time);
                }
            }
        }

        public void RemoveObserver(ObserverInterface o)//移除
        {
            if (observerList.Contains(o))
            {
                observerList.Remove(o);
            }
        }

    }

    public class ObserverOne:ObserverInterface
    {
        SubjectInterface subject;

        public ObserverOne(SubjectInterface subject)
        {
            this.subject = subject;
            this.subject.AddObserver(this);
        }

        public void Update(int time)
        {
            Console.WriteLine("ObserverOne ==" + time);
        }

        public void Remove() //移除自身
        {
            subject?.RemoveObserver(this);
        }
    }

    public class ObserverTwo : ObserverInterface
    {
        SubjectInterface subject;

        public ObserverTwo(SubjectInterface subject)
        {
            this.subject = subject;
            this.subject.AddObserver(this);
        }

        public void Update(int time)
        {
            Console.WriteLine(time +" = ObserverTwo ==" + time);
        }

        public void Remove()
        {
            subject?.RemoveObserver(this);
        }
    }
}
复制代码
  • 方式二
namespace Algorithm.Design
{
    class ObserverMode2
    {
        public static void Main()
        {
            Subject2 subject = new Subject2();
            ObserverOne2 one = new ObserverOne2();
            ObserverTwo2 two = new ObserverTwo2();
            subject.AddObserver(one.Update);
            subject.AddObserver(two.Update);

            Run(subject);
            Console.WriteLine("-==========================");
            subject.RemoveObserver(two.Update);
            Run(subject);
        }

        public static void Run(Subject2 subject)
        {
            for (int i = 0; i < 100; i++)
            {
                if (i == 44)
                {
                    subject.NotifyObserver(i);//通知观察者
                    break;
                }
            }
        }
    }

    public delegate void Update(int time); //定义委托

    public class Subject2
    {
        public event Update update;

        public void AddObserver(Update u)
        {
            if(update == null) //没初始化先初始化
            {
                update = new Update(u);
            }
            else //否则之间添加
            {
                update += u;
            }
        }

        public void NotifyObserver(int time)
        {
            if(update != null)
            {
                update(time);
            }
        }

        public void RemoveObserver(Update u)
        {
            if(update != null)
            {
                update -= u;
            }
        }
    }

    public class ObserverOne2
    {
        public void Update(int time)
        {
            Console.WriteLine("ObserverOne ==" + time);
        }
    }

    public class ObserverTwo2
    {

        public void Update(int time)
        {
            Console.WriteLine(time + " = ObserverTwo ==" + time);
        }
    }
}

复制代码
  • Subject2中保留了一个Update的委托对象,前面添加event的作用是使该类有更好的封装性。
  • 事件(event)封装了委托类型的变量,使得:在类的内部,不管你声明它是public还是protected,它总是private的。在类的外部,注册“+=”和注销“-=”的访问限定符与你在声明事件时使用的访问符相同
  • 避免在类外面以subject.update(2)这类的不恰当的形式直接触发委托。因为event只允许出现在+=和-=的左边,使类有更好的封装性。

推荐

顺便推荐一篇写的非常好的关于C#事件和委托的博文