观察者模式

# 分类

行为型模式

# 定义

  • 观察者模式(Observer Pattern) 也叫订阅-发布模式,当一个对象被修改时,则会自动通知依赖它的对象。

# 意图

定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

# 应用场景

  • 一个抽象模型有两个方面,其中一个方面依赖于另一个方面,这时用观察者模式可以将这两者封装在独立的对象中使它们各自独立地改变和复用。
  • 一个对象的改变将导致其他一个或者多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
  • 一个对象必须通知其他对象,而并不知道这些对象是谁。
  • 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象.....,可以使用观察者模式创建一种链式触发机制。

应用案例

  • 拍卖的时候,拍卖师观察最高标价,然后通知给其他竞价者竞价。
  • 西游记里孙悟空请求菩萨降服红孩儿,菩萨洒了一地水招来一个老乌龟,这个老乌龟就是观察者,他观察菩萨洒水动作。
  • 当一个对象的改变需要同时改变其他对象时,而且它不知道具体有多少对象有待改变时,应该考虑使用观察者模式。

# 角色与结构图

  • Subject:主题或者抽象通知者角色 一般为一个抽象类或者一个接口,它把所有对观察者对象的引用保存在一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。
  • Observer:抽象观察者角色 为所有的具体观察者定义一个接口,在得到主题的通知时更新自己。抽象观察者一般用一个抽象类或者一个接口实现。
  • ConcreteSubject:具体主题角色 具体主题或者具体通知者,将有关状态存入具体观察者对象,在具体主题内部状态发生变化时,给所有登记过的观察者发出通知。
  • ConcreteObserver:具体观察者角色 实现抽象观察者角色所要求的的接口,以便使本身的状态与主题的状态相协调。具体观察者角色可以保存一个指向具体主题对象的引用。

下图解释了观察者模式中各角色的作用

<<------------------------观察者模式结构图--------------------------->>

# 示例代码

//看股票行情的同事
class StockObserver{
  private string name;
  private Subject subject;

  public StockObserver(string name,Subject subject){
    this.name = name;
    this.sub=sub;
  }

  public void CloseStockMarket(){
    Console.WriteLine("{0}{1}关闭股票行情,继续工作!",subject.SubjectState,name);
  }
}

//看NBA的同事
class NBAObserver{
  private string name;
  private Subject subject;

  public NBAObserver(string name,Subject subject){
    this.name = name;
    this.sub=sub;
  }

  public void CloseNBADirectSeeding(){
    Console.WriteLine("{0}{1}关闭NBA直播,继续工作!",subject.SubjectState,name);
  }
}

//通知者接口
interface Subject{
  void Notify();
  string SubjectState{get;set;}
}

//定义事件
delegate void EventHandler();

//老板通知者
class Boss : Subject{
  public event EventHandler Update;

  private string action;

  public void Notify(){
    Update();
  }

  public string SubjectState{
    get{return action;}
    set{action = value;}
  }
}

//秘书通知者
class Secretary :Subject{
   public event EventHandler Update;

  private string action;

  public void Notify(){
    Update();
  }

  public string SubjectState{
    get{return action;}
    set{action = value;}
  }
}

//客户端
static void Main(string[] args){
  Boss huhansan = new Boss();

  StockObserver tongshi1 = new StockObserver("张三",huhansan);
  NBAObserver tongshi2 = new NBAObserver("李四",huhansan);

  huhansan.Update += new EventHandler(tongshi1.CloseStockMarket);
  huhansan.Update += new EventHandler(tongshi2.CloseNBADirectSeeding);

  huhansan.Subjectstate = "我胡汉三回来了";

  huhansan.Notify();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85

# 优点

  • 体现依赖倒置原则
  • 观察者和被观察者是抽象耦合的。
  • 建立一套触发机制。

# 缺点

  • 抽象通知者还是依赖抽象观察者,万一没了抽象观察者这样的接口,通知的功能就完不成了。
  • 观察者多时,会花费很多时间。
  • 如果在观察者和观察者目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统奔溃。
  • 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。

# 小结

  • 观察者模式所做的工作其实就是在解除耦合,让耦合的双方依赖于抽象,而不是依赖于具体。从而使得各自的变化都不会影响另一边的变化。

  • 使用委托可以解决通知时观察者回调方法不一致的问题。

  • 先有观测者模式,再有委托事件,解决问题的方法逐渐优雅完美

  • 主要解决?一个对象状态改变给其他对象通知的问题,而且主要考虑到易用和低耦合,保证高的协作。

  • 何时使用?一个对象的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。

  • 如何解决?使用面向对象技术,可以将这种依赖关系弱化。

  • 关键代码?在抽象类有一个列表存放观察者对象。

  • 注意避免循环引用。如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。

委托

  • 委托就是一种引用方法的类型,一旦为委托分配了方法,委托将与该方法具有完全相同的行为。
  • 委托方法的使用可以像其他任何方法一样,具有参数和返回值。
  • 委托可以看作是对函数的抽象,是函数的类,委托的实例将代表一个具体的函数。
  • 委托可以搭载多个方法,所有方法被依次唤起。更重要的是,它可以使得委托对象所搭载的方法并不需要属于同一个类。
  • 委托对象所搭载的所有方法必须具有相同的原型和形式,也就是拥有相同的参数列表和返回值类型。

# 一句话概括

对象间的一对多的依赖关系。

上次更新: 2025/03/22, 13:47:44
最近更新
01
Git问题集合
01-29
02
安装 Nginx 服务器
01-25
03
安装 Docker 容器
01-25
更多文章>
×
×