设计模式学习:Observer模式

一、概述:     

DP一书对Observer模式意图的描述是:“定义对象间的一种一对多依赖关系,使得每当一个对象改变装态,则其相关依赖对象皆得到通知并自动更新。”

在软件的构建过程中,我们常常要为一些对象建立一种通知依赖关系:当一个对象(Subject)的状态发生改变或某一特定事件发生时,所有的依赖对象(Observer)都需要得到通知。由于需求的变化,需要得到通知的Observer对象可能会发生变化,这是我们就需要使用面向对象的设计方法封装这种变化,使观察者和被观察者形成一种松散的耦合关系。

二、分析问题并寻求解决方案:

       根据面向对象的分析原则,我们应该分析问题中的变化因素并尝试封装之。我们发现“变化”主要来自两个方面

1)不同类型的对象:要获得通知的对象往往不属于相同的类型。

2)不同的接口:由于所属类型不同,往往导致出现不同的接口。

如果所有的观察者没有相同的接口,我们就必须修改目标对象(Subject)以通知各种不同类型的观察者(Observer)。这样做显然会导致目标对象的复杂化,而且会增加观察者和被观察者的耦合度。

为了统一观察者的类型,可以使用接口来封装不同的观察者。同时根据对象要对自己负责的原则,观察者(Observer)应该负责了解自己的观察目标(Subject)是什么,而目标对象(Subject)无需知道那些具体的观察者(Observer)依赖于自己。这就需要在目标对象中添加注册观察者的方法:

1)AttachObserver):将给定的Observer对象注册到目标对象的观察者列表。

2)DetachObserver):从目标对象的观察者列表中删除指定的Observer对象。

这样,由于目标对象(Subject)持有观察者(Object)的对象列表,当目标对象状态改变或特定事件发生时,就可以轻松地通知各种类型的观察者。我们可以在Subject中实现一个Notify方法遍历观察者列表,并调用每个Observer对象的Update方法(包含了相应的事件处理代码)。

三、观察者模式的UML类图:


1:观察者模式的UML类图

四、示例——股票和投资者

       假定我们开放一个简单的应用程序来跟踪股票的价格。我们指定一个Stock类来模拟股票交易市场上的股票,一个Investor类模拟各种不同的投资者。随着时间的变化股票的价格会发生变化,这种变化应该以Email、手机或其它方式通知所有已经注册的投资者。
1)不考虑设计模式的实现

  1 None.gif   // 被观察者:股票
  2 None.gif      public   class  Stock
  3 ExpandedBlockStart.gifContractedBlock.gif     dot.gif {
  4InBlock.gif        private InvestorWithEmail investorWithEmail;
  5InBlock.gif        private InvestorWithCell investorWithCell;
  6InBlock.gif
  7InBlock.gif        private string symbol;
  8InBlock.gif        private string price;
  9InBlock.gif
 10InBlock.gif        public Stock(string symbol, string price)
 11ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
 12InBlock.gif            this.symbol = symbol;
 13InBlock.gif            this.price = price;
 14ExpandedSubBlockEnd.gif        }

 15InBlock.gif
 16InBlock.gif        public string Symbol
 17ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
 18ExpandedSubBlockStart.gifContractedSubBlock.gif            get dot.gifreturn symbol; }
 19ExpandedSubBlockStart.gifContractedSubBlock.gif            set dot.gif{ symbol = value; }
 20ExpandedSubBlockEnd.gif        }

 21InBlock.gif        public string Price
 22ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
 23ExpandedSubBlockStart.gifContractedSubBlock.gif            get dot.gifreturn price; }
 24ExpandedSubBlockStart.gifContractedSubBlock.gif            set dot.gif{ price = value; }
 25ExpandedSubBlockEnd.gif        }

 26InBlock.gif
 27InBlock.gif        public InvestorWithEmail InvestorWithEmail
 28ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
 29ExpandedSubBlockStart.gifContractedSubBlock.gif            get dot.gifreturn investorWithEmail; }
 30ExpandedSubBlockStart.gifContractedSubBlock.gif            set dot.gif{ investorWithEmail = value; }
 31ExpandedSubBlockEnd.gif        }

 32InBlock.gif        public InvestorWithCell InvestorWithCell
 33ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
 34ExpandedSubBlockStart.gifContractedSubBlock.gif            get dot.gifreturn investorWithCell; }
 35ExpandedSubBlockStart.gifContractedSubBlock.gif            set dot.gif{ investorWithCell = value; }
 36ExpandedSubBlockEnd.gif        }

 37InBlock.gif
 38InBlock.gif        public void Update(Stock stock)
 39ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
 40InBlock.gif            //dot.gif.
 41InBlock.gif            investorWithEmail.SendEmail(stock);
 42InBlock.gif            investorWithCell.SendSM(stock);
 43ExpandedSubBlockEnd.gif        }

 44InBlock.gif
 45InBlock.gif
 46ExpandedBlockEnd.gif    }

 47 None.gif
 48 None.gif     // 观察者:需要以Email方式接受通知的投资者
 49 None.gif      public   class  InvestorWithEmail
 50 ExpandedBlockStart.gifContractedBlock.gif     dot.gif {
 51InBlock.gif        private string investorName;
 52InBlock.gif
 53InBlock.gif        public InvestorWithEmail(string investorName)
 54ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
 55InBlock.gif            this.investorName = investorName;
 56ExpandedSubBlockEnd.gif        }

 57InBlock.gif
 58InBlock.gif        public void SendEmail(Stock stock)
 59ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
 60InBlock.gif            Console.WriteLine("Notify:{0},The price of {1} was changed to:{2}!", stock.InvestorWithEmail.investorName, stock.Symbol, stock.Price);
 61ExpandedSubBlockEnd.gif        }

 62ExpandedBlockEnd.gif    }

 63 None.gif     // 观察者:需要以Cell方式接受通知的投资者
 64 None.gif      public   class  InvestorWithCell
 65 ExpandedBlockStart.gifContractedBlock.gif     dot.gif {
 66InBlock.gif        private string investorName;
 67InBlock.gif
 68InBlock.gif        public InvestorWithCell(string investorName)
 69ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
 70InBlock.gif            this.investorName = investorName;
 71ExpandedSubBlockEnd.gif        }

 72InBlock.gif
 73InBlock.gif        public void SendSM(Stock stock)
 74ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
 75InBlock.gif            Console.WriteLine("Notify:{0},The price of {1} was changed to:{2}!", stock.InvestorWithCell.investorName, stock.Symbol, stock.Price);
 76ExpandedSubBlockEnd.gif        }

 77ExpandedBlockEnd.gif    }

 78 None.gif
 79 None.gif     // 简单的客户端实现
 80 None.gif      public   class  StcokApp
 81 ExpandedBlockStart.gifContractedBlock.gif     dot.gif {
 82InBlock.gif        public static void Main()
 83ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
 84InBlock.gif            //创建观察者:不同的投资者
 85InBlock.gif            InvestorWithEmail investorWithEmail = new InvestorWithEmail("Mike");
 86InBlock.gif            InvestorWithCell investorWithCell = new InvestorWithCell("Tom");
 87InBlock.gif
 88InBlock.gif            //创建被观察者:股票
 89InBlock.gif            Stock stock = new Stock("MS""100");
 90InBlock.gif
 91InBlock.gif            stock.InvestorWithEmail = investorWithEmail;
 92InBlock.gif            stock.InvestorWithCell = investorWithCell;
 93InBlock.gif            stock.Update(stock);
 94InBlock.gif
 95InBlock.gif            Console.ReadKey();
 96InBlock.gif
 97ExpandedSubBlockEnd.gif        }

 98InBlock.gif
 99InBlock.gif
100ExpandedBlockEnd.gif    }

   运行结果:

   代码实现了我们设想的功能,把MS股票价格的变动通知到了不同的投资者MikeTom。但是我们经过细心研究,这里面仍然存在着几个问题:

      1)股票跟不同的投资者之间存在着一种强依赖关系。Stock需要调用InvetorWithEmailInvestorWithCell的方法,如果InvetorWithEmailInvestorWithCell发生变化可能导致Stock发生变化。

     2)如果出现新的通知方式,将不得不修改Stock类以适应新的需求。这违背了开放-封闭原则(软件实体应该是可以扩展的,但是不可以修改)。

 (2)如何解决存在的问题呢?根据我们在二中的分析,演化出如下的解决方案:


2:股票和投资者的类图

       这时具体股票和具体投资者的直接依赖关系变成了间接依赖关系,并且通过这种方式我们可以在不影响现有类的情况下,添加新的股票和投资者类型,保持了类间的较松耦合。

具体实现代码如下:

  1 None.gif   // 被观察者
  2 None.gif      public   abstract   class  Stock
  3 ExpandedBlockStart.gifContractedBlock.gif     dot.gif {
  4InBlock.gif        List<IInvestor> investorList = new List<IInvestor>();
  5InBlock.gif        
  6InBlock.gif        private string symbol;
  7InBlock.gif        private string price;
  8InBlock.gif
  9InBlock.gif        public Stock(string symbol, string price)
 10ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
 11InBlock.gif            this.symbol = symbol;
 12InBlock.gif            this.price = price;
 13ExpandedSubBlockEnd.gif        }

 14InBlock.gif
 15InBlock.gif        public string Symbol
 16ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
 17ExpandedSubBlockStart.gifContractedSubBlock.gif            get dot.gifreturn symbol; }
 18ExpandedSubBlockStart.gifContractedSubBlock.gif            set dot.gif{ symbol = value; }
 19ExpandedSubBlockEnd.gif        }

 20InBlock.gif        public string Price
 21ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
 22ExpandedSubBlockStart.gifContractedSubBlock.gif            get dot.gifreturn price; }
 23ExpandedSubBlockStart.gifContractedSubBlock.gif            set dot.gif{ price = value; }
 24ExpandedSubBlockEnd.gif        }

 25InBlock.gif
 26InBlock.gif        public IInvestor IInvestor
 27ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
 28InBlock.gif            get
 29ExpandedSubBlockStart.gifContractedSubBlock.gif            dot.gif{
 30InBlock.gif                throw new System.NotImplementedException();
 31ExpandedSubBlockEnd.gif            }

 32InBlock.gif            set
 33ExpandedSubBlockStart.gifContractedSubBlock.gif            dot.gif{
 34ExpandedSubBlockEnd.gif            }

 35ExpandedSubBlockEnd.gif        }

 36InBlock.gif
 37InBlock.gif        //附加投资者
 38InBlock.gif        public  void Attach(IInvestor investor)
 39ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
 40InBlock.gif            investorList.Add(investor);
 41ExpandedSubBlockEnd.gif        }

 42InBlock.gif        //移除投资者
 43InBlock.gif        public void Detach(IInvestor investor)
 44ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
 45InBlock.gif            investorList.Remove(investor);
 46ExpandedSubBlockEnd.gif        }

 47InBlock.gif        public void Notify(Stock stock)
 48ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
 49InBlock.gif            //dot.gif.
 50InBlock.gif            foreach (IInvestor investor in investorList)
 51ExpandedSubBlockStart.gifContractedSubBlock.gif            dot.gif{
 52InBlock.gif                investor.Update(this);
 53ExpandedSubBlockEnd.gif            }

 54ExpandedSubBlockEnd.gif        }

 55InBlock.gif
 56InBlock.gif
 57ExpandedBlockEnd.gif    }

 58 None.gif     // 具体的被观察者:股票
 59 None.gif      class  StockofMS : Stock
 60 ExpandedBlockStart.gifContractedBlock.gif     dot.gif {
 61InBlock.gif        public StockofMS(string symbol, string price)
 62InBlock.gif            : base(symbol, price)
 63ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
 64ExpandedSubBlockEnd.gif        }

 65ExpandedBlockEnd.gif    }

 66 None.gif
 67 None.gif     // 观察者
 68 None.gif      public   interface  IInvestor
 69 ExpandedBlockStart.gifContractedBlock.gif     dot.gif {
 70InBlock.gif         void Update(Stock stock);
 71ExpandedBlockEnd.gif    }

 72 None.gif     // 具体的观察者:需要以Email方式接受通知的投资者
 73 None.gif      public   class  InvestorWithEmail : IInvestor
 74 ExpandedBlockStart.gifContractedBlock.gif     dot.gif {
 75InBlock.gif        private string  investorName;
 76InBlock.gif
 77InBlock.gif        public InvestorWithEmail(string investorName)
 78ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
 79InBlock.gif            this.investorName = investorName;
 80ExpandedSubBlockEnd.gif        }

 81InBlock.gif
 82InBlock.gif        public void Update(Stock stock)
 83ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
 84InBlock.gif            Console.WriteLine("Notify:{0},The price of {1} was changed to:{2}!",this.investorName, stock.Symbol, stock.Price);
 85ExpandedSubBlockEnd.gif        }

 86ExpandedBlockEnd.gif    }

 87 None.gif     // 具体的观察者:需要以Cell方式接受通知的投资者
 88 None.gif      public   class  InvestorWithCell : IInvestor
 89 ExpandedBlockStart.gifContractedBlock.gif     dot.gif {
 90InBlock.gif        private string investorName;
 91InBlock.gif
 92InBlock.gif        public InvestorWithCell(string investorName)
 93ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
 94InBlock.gif            this.investorName = investorName;
 95ExpandedSubBlockEnd.gif        }

 96InBlock.gif
 97InBlock.gif        public void Update(Stock stock)
 98ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
 99InBlock.gif            Console.WriteLine("Notify:{0},The price of {1} was changed to:{2}!"this.investorName, stock.Symbol, stock.Price);
100ExpandedSubBlockEnd.gif        }

101ExpandedBlockEnd.gif    }

102 None.gif
103 None.gif     // 简单的客户端实现
104 None.gif      public   class  StcokApp
105 ExpandedBlockStart.gifContractedBlock.gif     dot.gif {
106InBlock.gif        public static void Main()
107ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
108InBlock.gif            //创建观察者:不同的投资者
109InBlock.gif            InvestorWithEmail investorWithEmail = new InvestorWithEmail("Mike");
110InBlock.gif            InvestorWithCell investorWithCell = new InvestorWithCell("Tom");
111InBlock.gif
112InBlock.gif            //创建被观察者:股票
113InBlock.gif            Stock stock = new StockofMS("MS""100");
114InBlock.gif
115InBlock.gif            //附加不同类型的观察者
116InBlock.gif            stock.Attach(investorWithEmail);
117InBlock.gif            stock.Attach(investorWithCell);
118InBlock.gif
119InBlock.gif            stock.Price = "119";
120InBlock.gif
121InBlock.gif            stock.Notify(stock);
122InBlock.gif
123InBlock.gif            Console.ReadKey();
124InBlock.gif
125ExpandedSubBlockEnd.gif        }

126InBlock.gif
127InBlock.gif
128ExpandedBlockEnd.gif    }

结论:

       观察者模式了被观察对象和观察者对象的连接,提供了广播式的对象间通信,并且容易增加/移除观察者对象。当需要得到某事件通知的Observer对象列表时变化的或者一个对象需要通知其它对象而又不需要掌握其它对象的详细信息时使用Observer模式十分合适。


转载于:https://www.cnblogs.com/wxf0701/archive/2006/12/21/599782.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值