动机(Motivate):
    在软件构建 过程中,我们需要为某些对象建立一种“通知依赖关系” --------一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知。如果这样的依赖关系过于紧密,将使软件不能很好地抵御变化。使用面 向对象技术,可以将这种依赖关系弱化,并形成一种稳定的依赖关系。从而实现软件体系结构的松耦合。
意图(Intent):
    
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动更新。
                                                                         -------《设计模式》GOF
结构图(Struct):
             
适用性:

1.当一个抽象模型有两个方面, 其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。

2.当对一个对象的改变需要同时改变其它对象, 而不知道具体有多少对象有待改变。

3.当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之, 你不希望这些对象是紧密耦合的。
生活中的例子:  

观 察者定义了对象间一对多的关系,当一个对象的状态变化时,所有依赖它的对象都得到通知并且自动地更新。在ATM取款,当取款成功后,以手机、邮件等方式进行通知。

    
代码实现:

 1     public class BankAccount
 2     {
 3         Emailer emailer;  //强信赖关系
 4         Mobile  phoneNumber;    //强信赖关系
 5        
 6         private double _money;
 7 
 8         public Emailer Emailer
 9         {
10             get { return emailer; }
11             set { this.emailer = value; }
12         }
13         public Mobile PhoneNumber
14         {
15             get { return phoneNumber; }
16             set { this.phoneNumber = value; }
17         }
18         public double Money
19         {
20             get { return _money; }
21             set { this._money = value; }
22         }
23   
24         public void WithDraw()
25         {
26             emailer.SendEmail(this);
27             phoneNumber.SendNotification(this);
28         }
29 
30     }
 1     public class Emailer
 2     {
 3        private string _emailer;
 4        public Emailer(string emailer)
 5        {
 6            this._emailer = emailer;
 7        }
 8         public void SendEmail(BankAccount ba)
 9         {
10             //..
11             Console.WriteLine("Notified : Emailer is {0}, You withdraw  {1:C} ", _emailer, ba.Money);
12         }
13     }
 1     public class Mobile
 2     {
 3         private long _phoneNumber;
 4         public Mobile(long phoneNumber)
 5         {
 6             this._phoneNumber = phoneNumber;
 7         }
 8         public void SendNotification(BankAccount ba)
 9         {
10             Console.WriteLine("Notified :Phone number is {0} You withdraw  {1:C} ", _phoneNumber, ba.Money);
11         }
12     }

此时简单的客户端调用如下:

 1 class Test
 2 {
 3     static void Main(string[] args)
 4     {
 5         BankAccount ba = new BankAccount();
 6         Emailer emailer = new Emailer("abcdwxc@163.com");       
 7         Mobile mobile = new Mobile(13901234567);
 8         ba.Emailer = emailer;
 9         ba.PhoneNumber = mobile;
10         ba.Money = 2000;       
11         ba.WithDraw();
12     }
13 }

运行结果如下:

    由此可见程序可以正常运行,但请注意BandAccount和Emailer及Mobile之间形成了一种双向的依赖关系,即BankAccount调用了Emailer及Mobile的方法,而Emailer及Mobile调用了BnadAccount类的属性。如果有其中一个类变化,有可能会引起另一个的变化。如果又需添加一种新的通知方式,就得在BankAccount的WithDraw()方法中增加对该中通知方式的调用。
    显然这样的设计极大的违背了“开放-封闭”原则,这不是我们所想要的,仅仅是新增加了一种通知对象,就需要对原有的BankAccount类进行修改,这样的设计是很糟糕的。对此做进一步的抽象,既然出现了多个通知对象,我们就为这些对象之间抽象出一个接口,用它来取消BankAccount和具体的通知对象之间依赖。
由此我们由左图转换到右图。
             
实例代码如下:

1   public interface IObserverAccount
2         {
3             void Update(BankAccount ba);
4         }
 1  public class BankAccount
 2         {
 3             IObserverAccount emailer;        //依赖于接口
 4             IObserverAccount phoneNumber;    //依赖于接口
 5 
 6             private double _money;
 7 
 8             public IObserverAccount Emailer
 9             {
10                 get { return emailer; }
11                 set { this.emailer = value; }
12             }
13             public IObserverAccount PhoneNumber
14             {
15                 get { return phoneNumber; }
16                 set { this.phoneNumber = value; }
17             }
18             public double Money
19             {
20                 get { return _money; }
21                 set { this._money = value; }
22             }
23 
24             public void WithDraw()
25             {
26                 emailer.Update(this);
27                 phoneNumber.Update(this);
28             }
29 
30         }
 1       public class Emailer : IObserverAccount
 2         {
 3             private string _emailer;
 4             public Emailer(string emailer)
 5             {
 6                 this._emailer = emailer;
 7             }
 8             public void Update(BankAccount ba)
 9             {
10                 //..
11                 Console.WriteLine("Notified : Emailer is {0}, You withdraw  {1:C} ", _emailer, ba.Money);
12             }
13         }
 1       public class Mobile : IObserverAccount
 2         {
 3             private long _phoneNumber;
 4             public Mobile(long phoneNumber)
 5             {
 6                 this._phoneNumber = phoneNumber;
 7             }
 8             public void Update(BankAccount ba)
 9             {
10                 Console.WriteLine("Notified :Phone number is {0} You withdraw  {1:C} ", _phoneNumber, ba.Money);
11             }
12         }

客户端与上方相同,其运行结果也相同。但BankAccount增加和删除通知对象时,还需对其进行修改。对此我们再做如下重构,在BankAccount中维护一个IObserver列表,同时提供相应的维护方法。

 1     public class BankAccount
 2     {
 3         private List<IObserverAccount> Observers = new List<IObserverAccount>();
 4 
 5 
 6         private double _money;
 7 
 8         public double Money
 9         {
10             get { return _money; }
11             set { this._money = value; }
12         }
13 
14         public void WithDraw()
15         {
16             foreach (IObserverAccount ob in Observers)
17             {
18                 ob.Update(this);
19 
20             }
21         }
22         public void AddObserver(IObserverAccount observer)
23         {
24             Observers.Add(observer);
25         }
26         public void RemoverObserver(IObserverAccount observer)
27         {
28             Observers.Remove(observer);
29         }
30 
31     }

此时客户端代码如下:

 1   class Test
 2     {
 3         static void Main(string[] args)
 4         {
 5             BankAccount ba = new BankAccount();
 6             IObserverAccount emailer = new Emailer("abcdwxc@163.com");
 7             IObserverAccount mobile = new Mobile(13901234567);
 8 
 9             ba.Money = 2000;
10             ba.AddObserver(emailer);
11             ba.AddObserver(mobile);
12 
13             ba.WithDraw();
14         }
15     }

走到这一步,已经有了Observer模式的影子了,BankAccount类不再依赖于具体的Emailer或Mobile,而是依赖于抽象的IObserverAccount。存在着的一个问题是Emailer或Mobile仍然依赖于具体的BankAccount,解决这样的问题很简单,只需要再对BankAccount类做一次抽象。如下图:
          

 1  public abstract class Subject
 2     {
 3         private List<IObserverAccount> Observers = new List<IObserverAccount>();
 4 
 5         private double _money;
 6         public Subject(double money)
 7         {
 8             this._money = money;
 9         }
10 
11         public double Money
12         {
13             get { return _money; }
14         }
15      
16         public void WithDraw()
17         {
18             foreach (IObserverAccount ob in Observers)
19             {
20                 ob.Update(this);
21 
22             }
23         }
24         public void AddObserver(IObserverAccount observer)
25         {
26             Observers.Add(observer);
27         }
28         public void RemoverObserver(IObserverAccount observer)
29         {
30             Observers.Remove(observer);
31         }
32 
33     }
1     public interface IObserverAccount
2     {
3         void Update(Subject subject);
4     }
1     public class BankAccount : Subject
2     {
3         public BankAccount(double money)
4             : base(money)
5         { }

7     }
 1     public class Emailer : IObserverAccount
 2     {
 3         private string _emalier;      
 4         public Emailer(string emailer )
 5         {
 6             this._emalier = emailer;           
 7         }
 8         public void Update(Subject subject)
 9         {            
10             Console.WriteLine("Notified : Emailer is {0}, You withdraw  {1:C} ", _emalier, subject.Money);
11         }
12     }
 1    public class Mobile : IObserverAccount
 2     {
 3         private long _phoneNumber;        
 4         public Mobile(long phoneNumber)
 5         {
 6             this._phoneNumber = phoneNumber;            
 7         }
 8         public void Update(Subject subject)
 9         {
10             Console.WriteLine("Notified :Phone number is {0} You withdraw  {1:C} ", _phoneNumber, subject.Money);
11         }
12     }

此时客户端实现如下:

 1    class Test
 2     {
 3         static void Main(string[] args)
 4         {
 5             Subject subject = new BankAccount(2000);
 6             subject.AddObserver(new Emailer("abcdwxc@163.com"));
 7             subject.AddObserver(new Mobile(13901234567));
 8 
 9             subject.WithDraw();
10         }
11     }

推模式与拉模式
    对于发布-订阅模型,大家都很容易能想到推模式与拉模式,用SQL Server做过数据库复制的朋友对这一点很清楚。在Observer模式中同样区分推模式和拉模式,我先简单的解释一下两者的区别:推模式是当有消息时,把消息信息以参数的形式传递(推)给所有观察者,而拉模式是当有消息时,通知消息的方法本身并不带任何的参数,是由观察者自己到主体对象那儿取回(拉)消息。知道了这一点,大家可能很容易发现上面我所举的例子其实是一种推模式的Observer模式。我们先看看这种模式带来了什么好处:当有消息时,所有的 观察者都会直接得到全部的消息,并进行相应的处理程序,与主体对象没什么关系,两者之间的关系是一种松散耦合。但是它也有缺陷,第一是所有的观察者得到的 消息是一样的,也许有些信息对某个观察者来说根本就用不上,也就是观察者不能“按需所取”;第二,当通知消息的参数有变化时,所有的观察者对象都要变化。鉴于以上问题,拉模式就应运而生了,它是由观察者自己主动去取消息,需要什么信息,就可以取什么,不会像推模式那样得到所有的消息参数。
拉模式实现如下:

 1 public abstract class Subject
 2     {
 3         private List<IObserverAccount> Observers = new List<IObserverAccount>();
 4 
 5 
 6         private double _money;
 7 
 8         public double Money
 9         {
10             get { return _money; }            
11         }
12         public Subject(double money)
13         {
14             this._money = money;
15         }
16         public void WithDraw()
17         {
18             foreach (IObserverAccount ob in Observers)
19             {
20                 ob.Update();
21 
22             }
23         }
24         public void AddObserver(IObserverAccount observer)
25         {
26             Observers.Add(observer);
27         }
28         public void RemoverObserver(IObserverAccount observer)
29         {
30             Observers.Remove(observer);
31         }
32 
33     }
1    public interface IObserverAccount
2     {
3         void Update();
4     }
1     public class BankAccount :Subject
2     {
3         public BankAccount(double money)
4             : base(money)
5         { }
6        
7     }
 1     public class Emailer : IObserverAccount
 2     {
 3         private string _emalier;
 4         private Subject _subject;
 5         public Emailer(string emailer,Subject subject)
 6         {
 7             this._emalier = emailer;
 8             this._subject = subject;
 9         }
10         public void Update()
11         {
12             //..
13             Console.WriteLine("Notified : Emailer is {0}, You withdraw  {1:C} ", _emalier,_subject.Money);
14         }
15     }
 1     public class Mobile : IObserverAccount
 2     {
 3         private long _phoneNumber;
 4         private Subject _subject;
 5         public Mobile(long phoneNumber,Subject subject)
 6         {
 7             this._phoneNumber = phoneNumber;
 8             this._subject = subject;
 9         }
10         public void Update()
11         {
12             Console.WriteLine("Notified :Phone number is {0} You withdraw  {1:C} ", _phoneNumber,_subject.Money);
13         }
14     }

此时客户端调用如下:

 1    class Test
 2     {
 3         static void Main(string[] args)
 4         {
 5            Subject subject= new BankAccount(2000);          
 6             subject.AddObserver(new Emailer("abcdwxc@163.com",subject));
 7             subject.AddObserver(new Mobile(13901234567,subject));
 8 
 9             subject.WithDraw();
10         }
11     }

.NET中Observer实现:
    
用事件和委托来实现Observer模式我认为更加的简单和优雅,也是一种更好的解决方案。

 1  public  class Subject
 2     {
 3         public event NotifyEventHandler NotifyEvent;
 4 
 5         private double _money;
 6         public Subject(double money)
 7         {
 8             this._money = money;
 9         }
10 
11         public double Money
12         {
13             get { return _money; }
14         }
15 
16         public void WithDraw()
17         {
18             OnNotifyChange();
19         }
20         public void OnNotifyChange()
21         {
22             if (NotifyEvent != null)
23             {
24                 NotifyEvent(this);
25             }
26 
27         }
28 
29     }
 1     public class Emailer
 2     {
 3         private string _emalier;
 4         public Emailer(string emailer)
 5         {
 6             this._emalier = emailer;
 7         }
 8         public void Update(object obj)
 9         {
10             if (obj is Subject)
11             {
12                 Subject subject = (Subject)obj;
13 
14                 Console.WriteLine("Notified : Emailer is {0}, You withdraw  {1:C} ", _emalier, subject.Money);
15             }
16         }
17 }
public delegate void NotifyEventHandler(object sender);

客户端调用如下:

 1     class Test
 2         {
 3             static void Main(string[] args)
 4             {
 5                 Subject subject = new Subject(2000);
 6                 Emailer emailer = new Emailer("abcdwxc@163.com");
 7                 subject.NotifyEvent += new NotifyEventHandler(emailer.Update);
 8            
 9 
10                 subject.WithDraw();
11             }
12         }

Observer实现要点:

1.使用面向对象的抽象,Observer模式使得我们可以独立地改变目标与观察者,从而使二者之间的依赖关系达到松耦合。

2.目标发送通知时,无需指定观察者,通知(可以携带通知信息作为参数)会自动传播。观察者自己决定是否需要订阅通知。目标对象对此一无所知。

3.在C#中的Event。委托充当了抽象的Observer接口,而提供事件的对象充当了目标对象,委托是比抽象Observer接口更为松耦合的设计。

#9楼 2007-12-25 16:08 wycg_cnh20
拉模式和推模式的差别在概念上讲得很清楚,但是,从代码上没看出推和拉的差别来/
支持(0) 反对(0)


  
#12楼 2009-04-17 14:30 soxyunyi
是否体现推拉,是由观察者类的update方法参数决定的。
支持(0) 反对(0)

#13楼 2009-04-17 14:34 soxunyi
public void update(Observable obs, Object arg) 
这是Java中Obersver接口方法, 
如果在方法体中通过obs来获取主题的状态变化,则是拉模式; 
如果在方法体中通过arg来获取主题的状态变化,则是推模式。 
因为,通过obs获取是在观察者类中实现的,故为拉。 
而arg则是由主题类传递给观察者的,故为推。

34观察者模式(Observer Pattern)相关推荐

  1. 设计模式 - 观察者模式(Observer Pattern) Java内置 用法

    观察者模式(Observer Pattern) Java内置 用法 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26601659 ...

  2. 设计模式-观察者模式(Observer Pattern)

    设计模式-观察者模式 观察者模式是使用频率最高的设计模式之一,它用于建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应作出反应.在观察者模式中,发生改变的对象称为观 ...

  3. 设计模式 -行为型模式_ 观察者模式Observer Pattern 之 JDK内置的实现

    文章目录 概念 Code [主题] [观察者s] 观察者一 观察者二 [测试] 源码分析 java.util.Observable 目标类 java.util.Observer 观察者接口 notif ...

  4. 设计模式学习--观察者模式(Observer Pattern)

    设计模式学习--观察者模式(Oberser Pattern) 2013年5月18日 天气:热!  下午15:28  设计模式学习中 学习者:小巫 什么是观察者模式? 定义了对象之间的一对多依赖,这样一 ...

  5. 极速理解设计模式系列:2.观察者模式(Observer Pattern)

    4个角色:被观察者(Subject/目标对象接口),具体被观察者(ConcreteSubject/具体目标对象),观察者(Observer),具体观察者(ConcreteObserver)       ...

  6. Java设计模式—观察者模式(Observer pattern)

    故事: 小雪是一个非常漂亮的女孩,漂亮的女孩总是有很多的追求者,而且追求者的队伍在不断的变动,随时有人进入这个队伍,也有人退出.男孩们追求女孩时总是表现出120%的关心,当小雪私自游玩时总是不断收到追 ...

  7. 锈才学设计模式之 —— 观察者模式(Observer Pattern)

    锈才学设计模式之  -- 观察者模式 观察者模式:定义对象的一对多的关系,这样当主题对象改变状态时,其它的观察者对象都会收到通知,自动更新. 说明: 在真实世界中存在很多类似的模型,比如:订报纸,找中 ...

  8. 设计模式(行为型)之观察者模式(Observer Pattern)

    PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN.因为CSDN也支持MarkDown语法了,牛逼啊! [工匠若水 http://blog.csdn.net/yanbob ...

  9. 观察者模式(Observer Pattern)(二):HeadFirst中的气象站的实现

    1 观察者模式的原理,首先由一个主题,当主题发送变化的时候,通知该主题的订阅者 按照上面的分析我们来进行设计 1.抽象主题Subject public interface Subject {publi ...

最新文章

  1. console 非常实用的方法
  2. .net 数字转汉字_[原创工具] 小熊汉字笔顺学习软件,查笔顺、学拼音、制作汉字英文数字字贴...
  3. Linux C :Linux 下第一个C程序
  4. 51cto 检测ip地址冲突_在Linux中发现IP地址冲突的方法
  5. java怎么把文件写入到usb里_如何创建PowerShell脚本以将文件复制到USB闪存驱动器?...
  6. 转: Linux下单网卡多vlan多虚拟机
  7. mongodb windows的安装方法和添加到任务管理器中、检测是否成功、增删改查命令...
  8. IHttpModule与IHttpHandler的区别整理
  9. jupyter调用py文件_解决Jupyter notebook中.py与.ipynb文件的import问题
  10. elementui表格列宽自适应_Java 操作Word表格——创建嵌套表格、添加复制表格行或列...
  11. JQuery 实战第三讲:绚丽菜单
  12. opencv3计算机视觉+Python(四)
  13. EntityFramework(EF) 单表与主从表的使用
  14. c语言多位数加减,两个超长正整数的加法
  15. wps for linux字体缺失,wps for linux 字体库缺失问题的解决办法
  16. Latex科技论文写作
  17. sql服务器显示空白,sql服务器空白
  18. 电热毯亚马逊欧洲站CE认证审核标准解析
  19. Steam自建游戏服务器配置
  20. windows防火墙是干什么的_防火墙的作用有哪些?Windows防火墙的主要作用是什么?...

热门文章

  1. python之os、sys和random模块
  2. Linux用户与“最小权限”原则
  3. mac下pip install 安装只能选择python2.7的问题
  4. [密码学基础][每个信息安全博士生应该知道的52件事][Bristol Cryptography][第36篇]Index Calculus算法
  5. Java学习笔记7-1——注解与反射
  6. bios设置开机双系统选择_打破专家的断言,突破微软和英特尔的封锁,惠普电脑玩转双系统...
  7. session很快失效_深夜,我偷听到程序员要对session下手...
  8. C语言#define宏定义可能注意不到的地方
  9. spring MVC配置form支持PUT和DELETE方法
  10. java setlocation_Java Point.setLocation方法代碼示例