.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 阅读(44261) 评论(175) 编辑 收藏

评论共2页: 上一页 1 2 
 回复 引用   
#63楼 2007-08-24 02:08 熊邵君[未注册用户]
写的相当好相当的简明 让我对观察者模式有了比较深刻的了解
希望笔者能够依照这种渐变的模式将设计模式都写一遍 不胜感激
非常好

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

VS里面自带的类图工具啊

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

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

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


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

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

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

如有不妥,请指教。



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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

 回复 引用   
#123楼 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类 去掉后, 它是不是就成为了策略模式了? 这么写还算得上是抽象工厂模式么? (见笑, 初学者初次接触设计模式)

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

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

 回复 引用   
#126楼 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支持用中文名称做类的声明和定义,呵)

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

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

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

谢谢.

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

 回复 引用   
#134楼 2009-05-22 14:29 wrfkltz[未注册用户]
好文章,很容易理解,谢谢
 回复 引用 查看   
#135楼 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), 这样的话, 在以后如果这个观察者想知道更多的信息的时候, 就只需要修改构造函数就可以了. 我是这样理解拉模式的, 不知道对不对.

请指教,
谢谢! :-)

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

 回复 引用   
#138楼 2009-07-29 17:10 Sunny Z[未注册用户]
人面不知何处去,桃花依旧笑春风!
 回复 引用   
#139楼 2009-08-17 11:12 lxy_10[未注册用户]
军哥,期待更新中,努力。。。
 回复 引用 查看   
#140楼 2009-08-25 18:37 无 宝 落 凤      
推与拉模式的例子比较失败,优点,缺点完全没有展示出来
 回复 引用 查看   
#141楼 2009-09-01 15:07 ☆会飞的鱼☆      
一直看李老帅的文章,讲的很好。呵呵。支持
楼主: 更新呀....
 回复 引用   
#143楼 2009-09-02 16:07 路过者[未注册用户]
写的很详细,加油吧,把这一套写完!
 回复 引用   
#144楼 2009-09-22 10:13 小迪张[未注册用户]
楼主不要偷懒,快把这个系列写完阿 :)
 回复 引用 查看   
#145楼 2009-09-27 22:46 ☆会飞的鱼☆      
这个主题写的很好,写的简单晚懂。
李老师,其他的什么时候能出来啊

 回复 引用 查看   
#146楼 2009-09-27 22:46 ☆会飞的鱼☆      
这个主题写的很好,写的简单晚懂。
李老师,其他的什么时候能出来啊

 回复 引用 查看   
#147楼 2009-10-24 14:22 medal      
看你的文章总有一种茅塞顿开的感觉
 回复 引用 查看   
#148楼 2009-11-03 19:02 只是找工的      
好东西,多多学习
 回复 引用 查看   
#149楼 2009-11-06 11:29 幻蓝      
好东西 3Q
 回复 引用 查看   
#150楼 2010-04-30 10:00 lzp      
学习了!
 回复 引用 查看   
#151楼 2010-07-30 11:26 不悔的青春      
实践,思考,重构,最终得到设计模式。学习模式一定要举一反三。多谢了,现在对设计模式已经有了新的认识。
 回复 引用 查看   
#152楼 2010-07-30 16:56 kuilong      
超赞,好好好,其三个好字了得
 回复 引用 查看   
#153楼 2010-10-29 10:48 周Jay      
我很喜欢看TerryLee写的文章,很好,有技术含量,而且通俗易懂,强烈支持!
 回复 引用 查看   
#154楼 2010-11-03 03:44 天上有云      
赶紧留下联系方式 。。
columbia sportswear outlet | north face outlet

 回复 引用 查看   
#155楼 2010-11-19 19:48 luge      
有一种眼前一亮的感觉……
 回复 引用 查看   
#156楼 2010-12-15 15:51 fangjie008      
看了好多遍了,支持
 回复 引用 查看   
#157楼 2011-01-20 13:51 大菜      
LZ 咋还不出下一篇呢?我都等了好多天啦
 回复 引用 查看   
#158楼 2011-07-29 17:00 b_mars      
非常受用 谢谢
 回复 引用 查看   
#159楼 2011-08-15 10:05 cpcpc      
期待下一章!!
 回复 引用 查看   
#160楼 2011-12-13 10:53 烟云过      
太牛啦 真的 不错 刚看了工厂模式 谢谢分享
 回复 引用 查看   
#161楼 2011-12-25 21:21 formme      
ms.AddObserver(new Investor("TerryLee",ms));
为什么拉模式要在这里 传递MS对象,感觉很怪;另外为什么Stock类没有响应的接口?是不是应该还有一个IObservable的接口?总感觉很怪,是不是自己理解有问题?

 回复 引用 查看   
#162楼 2012-01-05 10:07 A_明~坚持      
学习观察者模式MSDN上有一篇很不错的,可以看看
http://msdn.microsoft.com/zh-cn/library/ms954621.aspx
个人认为比楼主的文章写得好很多!
呵呵,楼主写得也很好了,但人家毕竟是官方的,都能学到东西就是好东西!
谢谢楼主笔迹!

评论共2页: 上一页 1 2