2012年8月23日星期四

Observer 观察者模式

Observer 观察者模式

1 GOF中的定义

意图

定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新。[GOF 《设计模式》]

结构图

2.概述

个人感觉观察者模式定义的比较不容易理解:什么多个观察者关注某个主题的,这个模式解决的是当一个对象需要调用一系列对象的方法时,并且是被调用方自己注册自己是否被调用,调用方不知道要调用哪些方法的问题。

3生活中的例子

现在,银行业务中有一项,当账号金额发生变化时就进行通知,默认是通知到手机,也可以选择将信息同时发送到邮箱。

4.调用方依赖被调用方

情景1:在这个业务的起初,账号金额变化,通知到手机

实现:

View Code
    public class Account    {        public void AmountChange()         {            Phone phone = new Phone();            phone.Send();        }    }    public class Phone    {        public void Send()         {            Console.WriteLine("Send phone!");        }    }        static void Main(string[] args)        {            Account account = new Account();            account.AmountChange();            }

说明:在这里,Account是调用方(即所谓的主题),Phone是被调用方(所谓的观察者)。在这里调用方强依赖被调用方。

情景2:业务发展了,账号金额变化,即通知手机,也通知邮箱。

实现:

View Code
    public class Account    {        public void AmountChange()        {            Phone phone = new Phone();            Email email = new Email();            phone.Send();            email.Send();        }    }    public class Email    {        public void Send()        {            Console.WriteLine("Send Email!");        }    }    public class Phone    {        public void Send()        {            Console.WriteLine("Send phone!");        }    }        static void Main(string[] args)        {            Account account = new Account();            account.AmountChange();        }

说明:在这里,新添加一个发送到邮箱的功能,必须要修改已有的Account类,违背了开放关闭原则。

5.调用方依赖接口,被调用方依赖接口(按照合约的设计)

情景3:重构情景2的代码

实现:

View Code
    public class Account    {        IList<IObserver> oList = new List<IObserver>();        public void Attach(IObserver observer)         {            oList.Add(observer);        }        public void AmountChange()        {            foreach (IObserver observer in oList)            {                observer.SendNotify();            }        }    }    public interface IObserver    {        void SendNotify();    }    public class Phone : IObserver    {        public void SendNotify()        {            Console.WriteLine("Send phone!");        }    }    public class Email:IObserver    {        public void SendNotify()        {            Console.WriteLine("Send Email!");        }    }static void Main(string[] args)        {            Account account = new Account();            Email email = new Email();            account.Attach(email);            Phone phone = new Phone();            account.Attach(phone);            account.AmountChange();        }

说明:在这里,调用方维护一个被调用方的列表,当需要调用被调用方时,只需要循环这个列表,即可;当新添加观察者时,只需要添加一个类继承IObserver接口,然后在客户端注册到IObserver列表中即可;

6.最后的重构(主动定阅)

情景4:在情景3中的实现,被调用方注册到调用列表的操作是在客户代码中实现的,被调用方被动注册,而在定义中被调用方是否被调用,是由被调用方自身决定,而不是调用方来决定。

实现:

View Code
    public class Account    {        IList<IObserver> oList = new List<IObserver>();        public void Attach(IObserver observer)         {            oList.Add(observer);        }        public void AmountChange()        {            foreach (IObserver observer in oList)            {                observer.SendNotify();            }        }    }    public interface IObserver    {        void SendNotify();    }    public class Phone : IObserver    {        public Account subject;        public Phone() { }        public Phone(Account subject)         {            this.subject = subject;            this.subject.Attach(this);        }        public void SendNotify()        {            Console.WriteLine("Send phone!");        }    }    public class Email : IObserver    {        public Account subject;        public Email() { }                public Email(Account subject)         {            this.subject = subject;            this.subject.Attach(this);        }        public void SendNotify()        {            Console.WriteLine("Send Email!");        }    }        static void Main(string[] args)        {            Account account = new Account();            Email email = new Email();            Phone phone = new Phone(account);            account.AmountChange();            return;        }

说明:在这里,被调用方是否被调用的决定决在被调用方;

7.总结

总的看来,Observer与Command十分相似,都是将行为抽象,让行为独立的变化。只不过在调用方是否调用被调用方的决定权有所不同,在Command中决定决在调用方,在Observer中决定权在被调用方。在Observer中,如果被调用的方法已在某个类实现,也可以像Command命令模式那样再融合下适配器模式,加入一个Receiver类。


TAG: