代码改变世界

Observer 观察者模式

2012-08-23 13:42  Mike.Jiang  阅读(568)  评论(0编辑  收藏  举报

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类。