.NET设计模式(19):观察者模式(Observer Pattern)

概述

在软件构建过程中,我们需要为某些对象建立一种“通知依赖关系” ——一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知。如果这样的依赖关系过于紧密,将使软件不能很好地抵御变化。使用面向对象技术,可以将这种依赖关系弱化,并形成一种稳定的依赖关系。从而实现软件体系结构的松耦合。

意图

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

结构图

1 Observer模式结构图

生活中的例子

观察者定义了对象间一对多的关系,当一个对象的状态变化时,所有依赖它的对象都得到通知并且自动地更新。拍卖演示了这种模式。每个投标人都有一个标有数字的牌子用于出价。拍卖师开始拍卖时,他观察是否有牌子举起出价。每次接受一个新的出价都改变了拍卖的当前价格,并且广播给所有的投标人进行新的出价。


2 使用拍卖例子的观察者模式

Observer模式解说

下面通过一个例子来说明Observer模式。监控某一个公司的股票价格变化,可以有多种方式,通知的对象可以是投资者,或者是发送到移动设备,还有电子邮件等。一开始我们先不考虑Observer模式,通过一步步地重构,最终重构为Observer模式。现在有这样两个类:MicrosoftInvestor,如下图所示:

3 UML静态图示例

它们的实现如下:

public class Microsoft

{
    
private Investor _investor;

    
private String _symbol;

    
private double _price;


    
public void Update()

    
{
        _investor.SendData(
this);
    }


    
public Investor Investor

    
{
        
get return _investor; }

        
set { _investor = value; }
    }


    
public String Symbol

    
{
        
get return _symbol; }

        
set { _symbol = value; }
    }


    
public double Price
    
{
        
get return _price; }

        
set { _price = value; }
    }

}



public class Investor

{
    
private string _name;

    
public Investor(string name)

    
{
        
this._name = name;
    }


    
public void SendData(Microsoft ms)

    
{
        Console.WriteLine(
"Notified {0} of {1}'s " + "change to {2:C}", _name, ms.Symbol,ms.Price);

    }


}

简单的客户端实现:

class Program

{

    
static void Main(string[] args)

    
{

        Investor investor 
= new Investor("Jom");

        Microsoft ms 
= new Microsoft();

        ms.Investor 
= investor;

        ms.Symbol 
= "Microsoft";

        ms.Price 
= 120.00;


        ms.Update();

        Console.ReadLine();

    }


}

运行后结果如下:

Notified Jom of Microsoft's change to 120

可以看到,这段代码运行并没有问题,也确实实现了我们最初的设想的功能,把Microsoft的股票价格变化通知到了Jom投资者那儿。但是这里面出现了如下几个问题:

1MicrosoftInvestor之间形成了一种双向的依赖关系,即Microsoft调用了Investor的方法,而Investor调用了Microsoft类的属性。如果有其中一个类变化,有可能会引起另一个的变化。

2.当出现一种的通知对象,比如说是移动设备Mobile

public class Mobile

{
    
private string _no;

    
public Mobile(string No)

    
{
        
this._no = No;
    }


    
public void SendData(Microsoft ms)

    
{
        Console.WriteLine(
"Notified {0} of {1}'s " + "change to {2:C}", _no, ms.Symbol, ms.Price);

    }


}

这时候对应的Microsoft的类就应该改变为如下代码,在Microsot类中增加Mobile,同时修改Update()方法使其可以通知到移动设备:

public class Microsoft

{
    
private Investor _investor;

    
private Mobile _mobile;

    
private String _symbol;

    
private double _price;
 

    
public void Update()

    
{
        _investor.SendData(
this);

        _mobile.SendData(
this);
    }


 

    
public Mobile Mobile

    
{
        
get return _mobile; }

        
set { _mobile = value; }
    }


    
public Investor Investor

    
{
        
get return _investor; }

        
set { _investor = value; }
    }
 

    
public String Symbol

    
{
        
get return _symbol; }

        
set { _symbol = value; }
    }


    
public double Price

    
{
        
get return _price; }

        
set { _price = value; }
    }


}

显然这样的设计极大的违背了“开放-封闭”原则,这不是我们所想要的,仅仅是新增加了一种通知对象,就需要对原有的Microsoft类进行修改,这样的设计是很糟糕的。对此做进一步的抽象,既然出现了多个通知对象,我们就为这些对象之间抽象出一个接口,用它来取消Microsoft和具体的通知对象之间依赖。

4 静态UML图示例

实现代码如下:

public interface IObserver

{
    
void SendData(Microsoft ms);
}


 

public class Investor : IObserver

{
    
private string _name;

    
public Investor(string name)

    
{
        
this._name = name;
    }


    
public void SendData(Microsoft ms)

    
{
        Console.WriteLine(
"Notified {0} of {1}'s " + "change to {2:C}", _name, ms.Symbol,ms.Price);

    }

}


public class Microsoft

{
    
private IObserver _investor;


    
private String _symbol;

    
private double _price;


    
public void Update()

    
{
        _investor.SendData(
this);
    }


 

    
public String Symbol

    
{
        
get return _symbol; }

        
set { _symbol = value; }
    }



    
public double Price

    
{
        
get return _price; }

        
set { _price = value; }
    }



    
public IObserver Investor

    
{
        
get return _investor; }

        
set { _investor = value; }
    }


}

做到这一步,可以看到,我们在降低两者的依赖性上已经迈进了一小步,正在朝着弱依赖性这个方向变化。在Microsoft类中已经不再依赖于具体的Investor,而是依赖于接口IObserver

但同时我们看到,再新出现一个移动设备这样的通知对象,Microsoft类仍然需要改变,对此我们再做如下重构,在Microsoft中维护一个IObserver列表,同时提供相应的维护方法。

5 静态UML示例图

Microsoft类的实现代码如下:

public class Microsoft

{
    
private List<IObserver> observers = new List<IObserver>();

    
private String _symbol;

    
private double _price;

    
public void Update()

    
{
        
foreach (IObserver ob in observers)
        
{
            ob.SendData(
this);
        }


    }


    
public void AddObserver(IObserver observer)

    
{
        observers.Add(observer);
    }


    
public void RemoveObserver(IObserver observer)

    
{
        observers.Remove(observer);
    }


    
public String Symbol

    
{
        
get return _symbol; }

        
set { _symbol = value; }
    }


    
public double Price

    
{
        
get return _price; }

        
set { _price = value; }
    }


}

此时客户端的调用代码:

class Program

{
    
static void Main(string[] args)

    
{
        IObserver investor1 
= new Investor("Jom");

        IObserver investor2 
= new Investor("TerryLee");

        Microsoft ms 
= new Microsoft();

        ms.Symbol 
= "Microsoft";

        ms.Price 
= 120.00;

        ms.AddObserver(investor1);

        ms.AddObserver(investor2);

        ms.Update();

        Console.ReadLine();
    }

}

走到这一步,已经有了Observer模式的影子了,Microsoft类不再依赖于具体的Investor,而是依赖于抽象的IOberver。存在着的一个问题是Investor仍然依赖于具体的公司Microsoft,况且公司还会有很多IBMGoogle等,解决这样的问题很简单,只需要再对Microsoft类做一次抽象。如下图所示:

6 静态UML示例图

实现代码如下:

public abstract class Stock

{
    
private List<IObserver> observers = new List<IObserver>();

    
private String _symbol;

    
private double _price;

    
public Stock(String symbol, double price)

    
{
        
this._symbol = symbol;

        
this._price = price;
    }


    
public void Update()

    
{
        
foreach (IObserver ob in observers)

        
{
            ob.SendData(
this);
        }


    }


    
public void AddObserver(IObserver observer)

    
{
        observers.Add(observer);
    }


    
public void RemoveObserver(IObserver observer)

    
{
        observers.Remove(observer);
    }


    
public String Symbol

    
{
        
get return _symbol; }
    }


    
public double Price

    
{
        
get return _price; }
    }

}


public class Microsoft : Stock

{

    
public Microsoft(String symbol, double price)

        : 
base(symbol, price)

    
{ }
}


public interface IObserver

{
    
void SendData(Stock stock);
}


public class Investor : IObserver

{
    
private string _name;

    
public Investor(string name)

    
{
        
this._name = name;
    }


    
public void SendData(Stock stock)

    
{
        Console.WriteLine(
"Notified {0} of {1}'s " + "change to {2:C}", _name, stock.Symbol,stock.Price);

    }


}

客户端程序代码如下:

class Program

{
    
static void Main(string[] args)

    
{
        Stock ms 
= new Microsoft("Microsoft",120.00);

        ms.AddObserver(
new Investor("Jom"));

        ms.AddObserver(
new Investor("TerryLee"));

        ms.Update();

        Console.ReadLine();
    }


}

到这里我们可以看到,通过不断的重构,不断地抽象,我们由一开始的很糟糕的设计,逐渐重构为使用Observer模式的这样一个方案。在这个例子里面,IOberser充当了观察者的角色,而Stock则扮演了主题对象角色,在任何时候,只要调用了StockUpdate()方法,它就会通知它的所有观察者对象。同时可以看到,通过Observer模式,取消了直接依赖,变为间接依赖,这样大大提供了系统的可维护性和可扩展性。

推模式与拉模式

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

public abstract class Stock

{
    
private List<IObserver> observers = new List<IObserver>();

    
private String _symbol;

    
private double _price;

    
public Stock(String symbol, double price)

    
{
        
this._symbol = symbol;

        
this._price = price;
    }


    
public void Update()

    
{
        
foreach (IObserver ob in observers)

        
{
            ob.SendData();
        }

    }


    
public void AddObserver(IObserver observer)

    
{
        observers.Add(observer);
    }


    
public void RemoveObserver(IObserver observer)

    
{
        observers.Remove(observer);
    }


    
public String Symbol

    
{
        
get return _symbol; }
    }


    
public double Price

    
{
        
get return _price; }
    }

}


public class Microsoft : Stock

{
    
public Microsoft(String symbol, double price)

        : 
base(symbol, price)
    
{ }
}


public interface IObserver

{
    
void SendData();
}


public class Investor : IObserver

{
    
private string _name;

    
private Stock _stock;

    
public Investor(string name,Stock stock)

    
{
        
this._name = name;

        
this._stock = stock;
    }


    
public void SendData()

    
{
        Console.WriteLine(
"Notified {0} of {1}'s " + "change to {2:C}", _name, _stock.Symbol, _stock.Price);

    }

}



class Program

{
    
static void Main(string[] args)

    
{
        Stock ms 
= new Microsoft("Microsoft"120.00);

        ms.AddObserver(
new Investor("Jom",ms));

        ms.AddObserver(
new Investor("TerryLee",ms));

        ms.Update();

        Console.ReadLine();
    }


}

当然拉模式也是有一些缺点的,主体对象和观察者之间的耦合加强了,但是这可以通过抽象的手段使这种耦合关系减到最小。[感谢idior的意见]

.NET中的Observer模式

.NET中,相信大家对于事件和委托都已经不陌生了,这里就不具体多说了。利用事件和委托来实现Observer模式我认为更加的简单和优雅,也是一种更好的解决方案。因为在上面的示例中我们可以看到,虽然取消了直接耦合,但是又引入了不必要的约束(暂且这么说吧)。即那些子类必须都继承于主题父类,还有观察者接口等。网上有很多这方面的例子,上面的例子简单的用事件和委托实现如下,仅供大家参考:

class Program
{
    
static void Main(string[] args)
    
{
        Stock stock 
= new Stock("Microsoft"120.00);

        Investor investor 
= new Investor("Jom");

        stock.NotifyEvent 
+= new NotifyEventHandler(investor.SendData);

        stock.Update();

        Console.ReadLine();
    }

}


public delegate void NotifyEventHandler(object sender);


public class Stock

{
    
public NotifyEventHandler NotifyEvent;

    
private String _symbol;

    
private double _price;

    
public Stock(String symbol, double price)

    
{
        
this._symbol = symbol;

        
this._price = price;
    }


    
public void Update()

    
{
        OnNotifyChange();    
    }


    
public void OnNotifyChange()

    
{
        
if (NotifyEvent != null)

        
{
            NotifyEvent(
this);
        }


    }


    
public String Symbol

    
{
        
get return _symbol; }
    }


    
public double Price

    
{
        
get return _price; }
    }

}


 

public class Investor

{
    
private string _name;

    
public Investor(string name)

    
{
        
this._name = name;
    }


    
public void SendData(object obj)

    
{
        
if (obj is Stock)

        
{
            Stock stock 
= (Stock)obj;

            Console.WriteLine(
"Notified {0} of {1}'s " + "change to {2:C}", _name, stock.Symbol, stock.Price);
        }


    }


}

效果及实现要点

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

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

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

适用性

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

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

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

总结

通过Observer模式,把一对多对象之间的通知依赖关系的变得更为松散,大大地提高了程序的可维护性和可扩展性,也很好的符合了开放-封闭原则。

参考资料

Erich Gamma等,《设计模式:可复用面向对象软件的基础》,机械工业出版社

Robert C.Martin,《敏捷软件开发:原则、模式与实践》,清华大学出版社

阎宏,《Java与模式》,电子工业出版社

Alan Shalloway James R. Trott,《Design Patterns Explained》,中国电力出版社

MSDN WebCast C#面向对象设计模式纵横谈(19)Observer 观察者模式(行为型模式)

 

更多设计模式文章请访问.NET设计模式系列

作者:TerryLee
出处:http://terrylee.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
posted @ 2006-10-23 23:49 TerryLee 阅读(23418) 评论(150)  编辑 收藏 网摘 所属分类: [05]  架构与设计

评论共2页: 上一页 1 2 
  回复  引用    
#101楼2007-03-07 08:16 | LL[未注册用户]
人气没有前面的模式旺呀!楼主还是抓时间把全部系列写完
  回复  引用  查看    
#102楼2007-03-19 11:21 | YanziMyWife      
Investor是不是就是一个Command
  回复  引用    
#103楼2007-04-17 10:50 | RongJun[未注册用户]
看了前面的"创建型模式专题",感觉设计模式确实不好学,而且还这么多个啊,

我想问问在项目的实际应用中,除了单件模式和工厂还有什么模式用的比较多啊

我想有个侧重点的学习.谢谢

  回复  引用    
#104楼2007-04-23 18:17 | YiLian[未注册用户]
这里的模式讲得太好了,清晰明了。
  回复  引用    
#105楼2007-05-27 12:32 | coda[未注册用户]
@TerryLee
1. 好像Microsoft&Investor的例子里面的方法名字和GOF的类图刚好是反的吧...虽然关系不大,但也许会让人误解...
2.我觉得你描述的推拉模式没有本质的区别,拉模式反而增加了类的耦合..

  回复  引用  查看    
#106楼2007-05-31 16:49 | 宝气狗      
@TerryLee
如何防止重复加事件呢?
stock.NotifyEvent += new NotifyEventHandler(investor.SendData);
stock.NotifyEvent += new NotifyEventHandler(investor.SendData);
stock.NotifyEvent += new NotifyEventHandler(investor.SendData);
我添加了三个相同的事件。会输出三行结果。
如何控制每个Investor的SendData只能被添加一次??
如果能控制了。耦合度也能降低吗?

  回复  引用  查看    
#107楼2007-05-31 20:38 | 宝气狗      
我加入了一个字典来维护。实验证明可行
public class Stock
{
   private Dictionary<object, NotifyEventHandler> _eventList;
   ...
}

  回复  引用    
#108楼2007-07-04 10:19 | yfshg[未注册用户]
受益匪浅!谢谢,可是我现在想通过Remoting技术实现服务器端和客户端的观察模式,不知道那位有高见。
  回复  引用    
#109楼2007-07-09 17:11 | txd[未注册用户]
@coda
同意,没有本质的区别~~都是把整个类实例送过去了,不同的是一个是通过方法的参数,一个是类的变量

  回复  引用  查看    
#110楼2007-07-12 11:27 | Anders06      
.NET中的Observer模式 里的这种通过事件实现的方式,我现在更加愿意理解为

Mediator
LZ,有何见解?

  回复  引用    
#111楼2007-08-18 20:43 | 八路[未注册用户]
我前天去書店買了,<軟件設計摘要和模式>這本書。我覺得挺好的。但是它缺少圖片和生活中的例子。理解起來沒你寫的好懂。你什么時候把后續的寫完,整理成書。我肯定會去買的,等你。
  回复  引用    
#112楼2007-08-20 13:01 | Scan[未注册用户]
请问这篇文章中的UML静态图是什么工具画的.
  回复  引用    
#113楼2007-08-20 13:24 | yinli[未注册用户]
@Scan 是用vs2005画的 ,在解决方案程序集名称上右键,选择 查看类关系图,就会生成一扩展名为.cd的类设计图,工具箱中有一个类设计器的选项卡,可以拖放控件进行编辑,编辑后,直接影响代码!
  回复  引用    
#114楼2007-08-24 02:08 | 熊邵君[未注册用户]
写的相当好相当的简明 让我对观察者模式有了比较深刻的了解
希望笔者能够依照这种渐变的模式将设计模式都写一遍 不胜感激
非常好

  回复  引用    
#115楼2007-08-28 09:23 | sparks345[未注册用户]
好久都没有更新了,加一下油~~~
^^V

  回复  引用    
#116楼2007-08-29 16:07 | 李一永[未注册用户]
我基本上从头到尾看了一遍,很是佩服你,也让我学到了不少,感谢TerryLee 的分享,希望你能有更好的文章。
  回复  引用    
#117楼2007-09-03 09:32 | 洛卜竺[未注册用户]
感激涕零...

看书看了N久都没看懂, 看你的文章 马上就茅塞顿开了

  回复  引用    
#118楼2007-09-12 15:25 | xx!![未注册用户]
其实这个系列的课程跟.net也是松耦合 或者说关系不大的
不如直接叫“设计模式纵横谈”好了

  回复  引用    
#119楼2007-09-24 11:05 | kscner2[未注册用户]
感觉作者举的推模式与拉模式实例耦合度上并无本质区别:
//推模式
public void SendData(Stock stock)
{
stock.XXX
……
}
//拉模式
private Stock _stock;
public void SendData()
{
_stock.XXXX
……
}
从以上两例可以看出。不管是拉还是推,他们的耦合度是一样的

  回复  引用  查看    
#120楼2007-09-29 15:11 | 偏执      
感激涕零...

看书看了N久都没看懂, 看你的文章 马上就茅塞顿开了

  回复  引用    
#121楼2007-12-04 16:01 | pyf[未注册用户]
难度高了点。。没怎么搞明白
  回复  引用    
#122楼2007-12-06 09:12 | Bamboo_Wan[未注册用户]
Terry兄,继续写下去啊。求了你,我上瘾了......
这些天天天过来逛一下,还是到观察者模式就没有了。
喷血期待中......

  回复  引用    
#123楼2007-12-06 17:09 | 名字都被注册了[未注册用户]
楼主,跪求你继续写完剩下的设计模式!
  回复  引用    
#124楼2007-12-08 16:13 | ray-----[未注册用户]
支持lz继续写
  回复  引用    
#125楼2007-12-13 16:22 | Bamboo_Wan[未注册用户]
写啊写啊写啊。。。。。。
  回复  引用    
#126楼2007-12-19 12:52 | 天行健 君子[未注册用户]
希望 版主把剩下的设计模式 写完!
  回复  引用    
#127楼2007-12-20 14:13 | jess.lv[未注册用户]
好东西,收藏起来
  回复  引用    
#128楼2007-12-27 11:52 | animal[未注册用户]
其实,感觉把Stock类抽象出来,与观察者模式并无多大关系吧.我倒是觉得这只是符合了面向对象的编程思想而已.另外,把公司类继承自股票类,个人觉得并不是很好,股票类或许应该只需写成接口.
  回复  引用  查看    
#129楼2008-01-02 14:49 | EricWen      
容易理解
  回复  引用  查看    
#130楼2008-01-08 17:38 | 杨森      
好东西。简明易懂,希望李大哥把后面的写完!
  回复  引用    
#131楼2008-01-11 14:08 | Bamboo_Wan[未注册用户]
TerryLee兄,不知道近一年在忙些什么,可能确实是没时间更新博客吧。
希望能吧接下来的设计模式写完。
不甚感激!!

  回复  引用    
#132楼2008-01-14 20:32 | 小纪哥[未注册用户]
TerryLee兄,你好
看了你的设计模式系列文章,感到学到很多东西,非常感谢。
目前我在开发一个Web服务应用,碰到了一个观察者模式的问题,不知道你能不能帮忙提供个思路。
观察者模式能用在web服务和智能客户端之间通信吗?比如web服务端改变了某个对象的信号值,智能客户端就能得到通知。
多谢。

  回复  引用    
#133楼2008-01-24 14:04 | Bamboo_Wan[未注册用户]
@小纪哥
当然可以,而且没有上面的那么复杂。
就一个观察者:SmartClient
一个通知者 :web服务。
分别将SmartClient和web服务创建为观察者类和通知者类,仿照上面的代码即可。

  回复  引用    
#134楼2008-02-14 09:56 | PeterChen1984[未注册用户]
呵呵,终于看完设计模式了。感谢TerryLee使我收益匪浅。
:) 请楼主继续努力,写完这23种设计模式啊。

  回复  引用    
#135楼2008-02-25 09:22 | 菜鸟毛[未注册用户]
使我得到不少的知识。呵呵,我想问一个很弱的问题,希望李大师回答啊,public string Symbol
{
get{ return symbol; }
set{ symbol = value; }
}

这个是不是这个属性的获得与设置的意思啊

  回复  引用    
#136楼2008-02-28 13:44 | Emcisq[未注册用户]
好文章,收藏
  回复  引用  查看    
#137楼2008-02-29 12:22 | wuhang      
觉得推拉模式结合例子后有点糊涂....
不过还是感谢TreeLee的好文章,结合事件代理就很明了了~!
支持~!

  回复  引用    
#138楼2008-03-21 13:35 | 星梦起源[未注册用户]
请问你这儿的UML图是使用什么工具画出来的?太好看了!!!
  回复  引用  查看    
#139楼[楼主]2008-03-21 22:14 | TerryLee      
@星梦起源
不是吧?

VS里面自带的类图工具啊

  回复  引用    
#140楼2008-04-19 23:37 | aadon[未注册用户]
太牛了。。。
期待着PDF收藏啊,牛逼。。。

  回复  引用  查看    
#141楼2008-05-05 17:22 | badnewfish      
理解推模式的要点是:
订阅者通过主题的订阅方法初始化,并加入到主题的订阅者列表中。
主题通过遍历这个列表来修改每个订阅者的信息。
也就是说,在整个模式实现过程中,订阅者本身是靠主题对象来初始化的。
如果从现实角度来讲,和实际生活似乎有些违背:
1、因为订阅者本身不能依赖主题的产生而产生;
2、主题没有提供给订阅者的订阅接口。

有没有这样一种模式:
订阅者自己去订阅主题,主题主动去更新订阅者的信息?


  回复  引用  查看    
#142楼2008-05-05 17:46 | badnewfish      
拉模式似乎合理一些,他在初始化订阅者的的时候告知了它拥有什么样的主题。
这个和现实生活更贴切一些,也便于理解.

  回复  引用    
#143楼2008-05-23 11:34 | NET_123.5[未注册用户]
怎么好久都没有更新了?
期待...............

  回复  引用  查看    
#144楼2008-05-24 11:07 | wayich      
希望能更新,我的爱,我等待。。。。。
  回复  引用    
#145楼2008-05-25 21:23 | 葫芦画瓢[未注册用户]
支持,为什么好久没更新了啊?
  回复  引用  查看    
#146楼2008-06-14 01:03 | 包建强      
老李,我来踢场子了,
你那个观察者模式,竞标的例子,不妥,那是观察者模式的忌讳:双向观察,从而导致死循环。就是说 敲槌子的人也要观察每个竞标者。我想想,换一个模式,对,是中介者,更好一些。
observer是mediator的特例,WebForm就是一个mediator。敲槌子的人,就像一个Form窗体;而每个竞标者,是Form窗体的一个控件。他们之间互相影响,是通过Form这个中介来传递的。
所以说,敲槌子的人,中介的性质更浓一些。不适合作为Observer的示例。

如有不妥,请指教。



  回复  引用  查看    
#147楼2008-06-26 13:57 | 小猴子      
呵呵,没白费我看到最后的评论。。。。
双向观察可以借鉴通讯模式,采用主从模式来避免死循环。

LZ的模式写简单易懂,却又不失内涵,实在难得!

  回复  引用    
#148楼2008-07-10 02:08 | ivor[未注册用户]
没细看,但也基本看懂,个人喜欢用委托处理
  回复  引用  查看    
#149楼2008-07-25 09:25 | 陈晨      
看了文章,感觉有委托--事件机制实现更好理解,代码也很简洁
可以委托--事件机制代替传统的观察着模式吗?

  回复  引用  查看    
#150楼[楼主]2008-07-25 09:48 | TerryLee      
@陈晨
事件机制本身也是使用了观察者模式,所以你用事件来实现没有什么问题。

  回复  引用  查看    
#151楼2008-07-25 10:25 | 陈晨      
@TerryLee
-----------------------
回复的速度真快^_^

  回复  引用  查看    
#152楼2008-08-01 10:34 | rose_Q      
呵呵!花了两天时间看完了你这个系列!做为初学者!我感到受益非浅!动手学习前,我还是喜欢从理论上系统理解下!感谢!无私的丰献!
  回复  引用  查看    
#153楼[楼主]2008-08-01 10:37 | TerryLee      
@rose_Q
太客气了:)

  回复  引用    
#154楼2008-08-05 14:49 | 远方qidai[未注册用户]
博主继续写嘛,急着看下文呢,坚持写下去哦,期待...
  回复  引用  查看    
#155楼[楼主]2008-08-06 13:21 | TerryLee      
@远方qidai
有些忙,要写的东西太多~~~

  回复  引用    
#156楼2008-08-06 17:49 | winterboy[未注册用户]
在张逸的书中,观察者向主题注册的代码是在观察者类中实现的,而博主是在运行时才将观察者注册到主题,像:stock.NotifyEvent += new NotifyEventHandler(investor.SendData);博主是写在main方法中,如果按照书中说的,会直接写在inversor里.

  回复  引用    
#157楼2008-08-06 17:50 | winterboy[未注册用户]
究竟是哪种方法更合理呢?
  回复  引用  查看    
#158楼[楼主]2008-08-13 21:49 | TerryLee      
@winterboy
没有哪个更合理,要看哪个更适合你的应用场景。

  回复  引用    
#159楼2008-08-30 17:03 | 初学者-蒋[未注册用户]
盼望楼主能把后续的帖子洗完,你写的太经典了!
  回复  引用    
#160楼2008-08-31 21:40 | wwzwcy[未注册用户]
楼主对设计模式理解的很透彻,但是我还有点问题想请教一下。
  回复  引用  查看    
#161楼2008-08-31 21:51 | wwz      
楼主对设计模式理解的很透彻,但是我还有点问题想请教一下。
private List<IObserver> observers = new List<IObserver>();
observers在多线程的情况下,如何能保证对象池中的监听对象能够不出现死锁问题。因为能用到观察者模式的地方很多都是在服务器端,而在服务端不可能不用到多线程。

  回复  引用    
#162楼2008-09-01 13:02 | 陈炳童[未注册用户]
楼主或其他人帮忙看一下我是初学者,想按照您的代码在自己机器上跑一遍,但出现了提示找不到类型或命名空间名称"Ivester"(是否缺少 using 指令或程序集引用?)
备注:就是刚开头那段代码

  回复  引用  查看    
#163楼[楼主]2008-09-01 22:00 | TerryLee      
@wwz
如果涉及到多线程,那就是线程死锁的相关问题处理了,而跟Observer Pattern关系不大。

  回复  引用  查看    
#164楼[楼主]2008-09-01 22:01 | TerryLee      
@陈炳童
晕倒,还有第二部分代码呢,Investor代码在下面
public class Investor

  回复  引用    
#165楼2008-10-30 23:15 | 酷酷![未注册用户]
不错,支持,一定要继续。。。。。。
  回复  引用    
#166楼2008-11-01 02:08 | Steven123[未注册用户]
这么好的一系列的文章,我竟然现在才看到,真是罪过...

感谢博主的奉献精神...希望可以继续看到博主更精彩的文章

  回复  引用  查看    
#167楼[楼主]2008-11-05 09:46 | TerryLee      
@酷酷!
有时间一定继续:)

  回复  引用  查看    
#168楼[楼主]2008-11-05 09:47 | TerryLee      
@Steven123
客气了,呵呵

  回复  引用  查看    
#169楼2008-11-26 12:44 | 自强不息      
下面的模式怎么不写了,期待中.....
  回复  引用  查看    
#170楼[楼主]2008-12-01 00:02 | TerryLee      
@自强不息
要写的东西太多了。。。,呵呵

  回复  引用  查看    
#171楼2008-12-26 11:07 | Fengdesudu      
不错,易懂。谢谢
  回复  引用  查看    
#172楼[楼主]2009-01-04 11:33 | TerryLee      
@Fengdesudu
:)

  回复  引用    
#173楼2009-01-09 12:58 | bruce_zhang[未注册用户]
楼主加我?
bruce_lingonova@hotmail.com

  回复  引用    
#174楼2009-01-09 14:10 | 灰鸽[未注册用户]
.NET设计模式(3):抽象工厂模式(Abstract Factory) 的评论系统好像不可用, 每次提交都提示验证码错误. 特附在这里试试.

你在抽象工厂模式(Abstract Factory)里的有关计算工资的代码是不是不全? 我下载不了你的源程序, 只好把你的代码一段段地COPY出来编译, 结果编译出一堆错误(见谅). 后来我结合 #46楼 的 [沧桑雨迢迢] 和 你的案例, 重新琢磨一下. 请指教:

/*
*
* 公司名称 : ***
* 用 户 名 : 枫中玫瑰
* 创建日期 :2009-1-6 16:25
* 程序名称 :AbstractFactory模式
*
*/

using System;
using System.Text;
using System.Reflection;

namespace ConsoleApplication
{
class ChineseCode
{
public static void Main()
{
Factory mySalary = Factory.GetInstance();

double bonusValue = mySalary.CalculateBonus();
double taxValue = mySalary.CalculateTax();
double salary = Constant.BASE_SALARY + bonusValue - taxValue;

Console.WriteLine( Constant.FACTORYNAME.Substring( 0, Constant.FACTORYNAME.Length-7 ) + " Salary is:" + salary);
}
}

#region Abstract Factory模式(抽象工厂)
/// <summary>
/// 公用的常量
/// </summary>
public class Constant
{
public static double BASE_SALARY = 4000;
public static string FACTORYNAME = "ChineseFactory"; //因未使用配置文件, 帮此处用于指定工厂
}

/// <summary>
/// 抽象工厂
/// </summary>
public abstract class Factory
{
public static Factory GetInstance()
{
Factory instance;
string assemblyName = "ConsoleApplication";
string factoryName = assemblyName + "." + Constant.FACTORYNAME.ToString();
//不知为何, 此处使用 factoryName = Constant.FACTORYNAME.ToString(); 会造成NullReferencException异常

if (factoryName != "")
instance = (Factory)Assembly.Load(assemblyName).CreateInstance(factoryName);
else
instance = null;

return instance;
}

public abstract double CalculateBonus();
public abstract double CalculateTax();
}

/// <summary>
/// 中国工厂
/// </summary>
public class ChineseFactory : Factory
{
//计算工资
public override double CalculateBonus()
{
return Constant.BASE_SALARY * 0.1;
}

// 计算个人所得税
public override double CalculateTax()
{
return ((Constant.BASE_SALARY + Constant.BASE_SALARY * 0.1)*0.4);
}
}

//// <summary>
/// 美国工厂
/// </summary>
public class AmericanFactory : Factory
{
//计算工资
public override double CalculateBonus()
{
return Constant.BASE_SALARY * 0.15;
}

// 计算个人所得税
public override double CalculateTax()
{
return (Constant.BASE_SALARY*0.05 + Constant.BASE_SALARY * 0.25);
}
}
#endregion
}

说明: 该程序是使用CSC进行单文件编译的.
疑问: 1. 在 CreateInstance 时指定的 factoryName , 为什么添加指定 assemblyName ; 是不是跟我的单文件编译有关?
2. 当我把 Factory类 去掉后, 它是不是就成为了策略模式了? 这么写还算得上是抽象工厂模式么? (见笑, 初学者初次接触设计模式)

  回复  引用  查看    
#175楼[楼主]2009-01-12 11:18 | TerryLee      
@灰鸽
1.在做反射时使用Assembly.Load方法,不知道程序集名如何反射?
2.策略模式与抽象工厂模式还是有些区别的。

  回复  引用    
#176楼2009-02-22 15:48 | wuSpark[未注册用户]
最近一直在看您的设计模式的文章。感觉受益匪浅。
很冒昧的问一问,后面的模式您还会不会继续写下去?

  回复  引用    
#177楼2009-02-25 17:48 | lfow[未注册用户]
public interface 被观察者接口
{
void 注册观察者(观察者接口 instance);
void 通知所有已注册的观察者();
}
public interface 观察者接口
{
void 来自被观察者的通知();
}

public class 被观察者的实现 : 被观察者接口
{
// 实现省略……
}

public class 观察者的实现 : 观察者接口
{
// 实现省略……
}

观察者的实现 instance1 = new 观察者的实现();
被观察者的实现 instance2 = new 被观察者的实现 ();
instance2.注册观察者(instance1);
//当instance2的内部状态改变时,会调用方法instance2.通知所有已注册的观察者(),该方法内部会调用方法 instance1.来自被观察者的通知();

这样比较好理解
(注:DotNet支持用中文名称做类的声明和定义,呵)

  回复  引用  查看    
#178楼2009-03-09 10:47 | haorenls      
您的每个模式介绍我都看了,写的简单易懂,学到了很多知识谢谢。
  回复  引用    
#179楼2009-04-07 11:37 | CountaMa[未注册用户]
以前在MSDN WebCast 看过李建中将的“观察者模式”,现在再看你的,又有了更深的理解,受益匪浅啊。
  回复  引用  查看    
#180楼2009-05-08 09:21 | BenBen789      
不错不错,学习中。
怎么好久都不更新了?在等呢。。。

  回复  引用    
#181楼2009-05-17 21:52 | tomchen
楼主辛苦了,写的真是太好了! :) 急盼继续更新....
  回复  引用  查看    
#182楼[楼主]2009-05-18 11:12 | TerryLee      
@tomchen
谢谢,有时间继续更新 :)

  回复  引用  查看    
#183楼2009-05-22 10:46 | Soleil      
一个疑问:
对于主体, 应不应该有一个 list 来存放所有的观察者呢?
如果不应该有, 那在程序中, 对观察者 注册 和 解除注册 的维护, 需要独立出来么?--- 比如一个类, 专门负责观察者的维护.

谢谢.

  回复  引用  查看    
#184楼[楼主]2009-05-22 13:46 | TerryLee      
@Soleil
观察者的维护,放在哪儿都可以,你可以看一下文章中的代码。

  回复  引用    
#185楼2009-05-22 14:29 | wrfkltz[未注册用户]
好文章,很容易理解,谢谢
  回复  引用  查看    
#186楼2009-05-22 14:39 | Soleil      
--引用--------------------------------------------------
TerryLee: @Soleil
观察者的维护,放在哪儿都可以,你可以看一下文章中的代码。
--------------------------------------------------------
可能是我表述不很清楚
我的意思是, 在.Net 中, 使用了 Event

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

这样, 就是说, 在主体类中, 不应该有观察者的信息. 主体只是Raise Event, 由 观察者来 Handle. 不知道我理解的对不对. 这样的话, 对观察者的维护 就只能独立于 主体 和 观察者.

static void Main(string[] args)
{
Stock stock = new Stock("Microsoft", 120.00);
Investor investor = new Investor("Jom");
stock.NotifyEvent += new NotifyEventHandler(investor.SendData);
stock.Update();
}
观察者的维护, 只能独立出来, 对么?

另外还有一个问题: 关于拉模式
拉模式, 在主体发送通知的时候, 是不带参数的, 那么, 在使用Event 的时候, 如何使用拉模式?(Event 函数前面不应该是 object sender, EventArgs e么? 这个e 应该就是 主体的信息. 但用拉模式的时候, 虽然不要传主体, 但此参数应该保留吧?)
我的理解是, 如果要使用拉模式, 就必须在观察者的类中, 包含主体的信息. 在执行观察者构造函数的过程中, 传入观察者需要的信息 如 : Investor investor = new Investor("Jom", stock.Price), 这样的话, 在以后如果这个观察者想知道更多的信息的时候, 就只需要修改构造函数就可以了. 我是这样理解拉模式的, 不知道对不对.

请指教,
谢谢! :-)

  回复  引用  查看    
#187楼2009-06-16 16:46 | Xproer-松鼠      
写的非常好,支持!
  回复  引用  查看    
#188楼2009-06-18 17:54 | caijinxing      
楼主懂得东西真多 拜师来啦
李老师辛苦了 网民感谢你

评论共2页: 上一页 1 2 



发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 537778




相关文章:

相关链接: