Observer模式

一、 观察者(Observer)模式

观察者模式又叫做发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。

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

一个软件系统常常要求在某一个对象的状态发生变化的时候,某些其它的对象做出相应的改变。做到这一点的设计方案有很多,但是为了使系统能够易于复用,应该选择低耦合度的设计方案。减少对象之间的耦合有利于系统的复用,但是同时设计师需要使这些低耦合度的对象之间能够维持行动的协调一致,保证高度的协作(Collaboration)。观察者模式是满足这一要求的各种设计方案中最重要的一种。

二、 观察者模式的结构

观察者模式的类图如下:

PicX00105

可以看出,在这个观察者模式的实现里有下面这些角色:

  • 抽象主题(Subject)角色:主题角色把所有对观察考对象的引用保存在一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象,主题角色又叫做抽象被观察者(Observable)角色,一般用一个抽象类或者一个接口实现。
  • 抽象观察者(Observer)角色:为所有的具体观察者定义一个接口,在得到主题的通知时更新自己。这个接口叫做更新接口。抽象观察者角色一般用一个抽象类或者一个接口实现。在这个示意性的实现中,更新接口只包含一个方法(即Update()方法),这个方法叫做更新方法。
  • 具体主题(ConcreteSubject)角色:将有关状态存入具体现察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色又叫做具体被观察者角色(Concrete Observable)。具体主题角色通常用一个具体子类实现。
  • 具体观察者(ConcreteObserver)角色:存储与主题的状态自恰的状态。具体现察者角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。如果需要,具体现察者角色可以保存一个指向具体主题对象的引用。具体观察者角色通常用一个具体子类实现。

从具体主题角色指向抽象观察者角色的合成关系,代表具体主题对象可以有任意多个对抽象观察者对象的引用。之所以使用抽象观察者而不是具体观察者,意味着主题对象不需要知道引用了哪些ConcreteObserver类型,而只知道抽象Observer类型。这就使得具体主题对象可以动态地维护一系列的对观察者对象的引用,并在需要的时候调用每一个观察者共有的Update()方法。这种做法叫做"针对抽象编程"。

三、 观察者模式的示意性源代码

// Observer pattern -- Structural example  
using System;
using System.Collections;

// "Subject"
abstract class Subject
{
// Fields
private ArrayList observers = new ArrayList();

// Methods
public void Attach( Observer observer )
{
    observers.Add( observer );
  }

public void Detach( Observer observer )
{
    observers.Remove( observer );
  }

public void Notify()
{
foreach( Observer o in observers )
      o.Update();
  }
}

// "ConcreteSubject"
class ConcreteSubject : Subject
{
// Fields
private string subjectState;

// Properties
public string SubjectState
{
get{ return subjectState; }
set{ subjectState = value; }
  }
}

// "Observer"
abstract class Observer
{
// Methods
abstract public void Update();
}

// "ConcreteObserver"
class ConcreteObserver : Observer
{
// Fields
private string name;
private string observerState;
private ConcreteSubject subject;

// Constructors
public ConcreteObserver( ConcreteSubject subject,  
string name )
{
this.subject = subject;
this.name = name;
  }

// Methods
override public void Update()
{
    observerState = subject.SubjectState;
    Console.WriteLine( "Observer {0}'s new state is {1}",
      name, observerState );
  }

// Properties
public ConcreteSubject Subject
{
get { return subject; }
set { subject = value; }
  }
}

/// <summary>
/// Client test
/// </summary>
public class Client
{
public static void Main( string[] args )
{
// Configure Observer structure
    ConcreteSubject s = new ConcreteSubject();
    s.Attach( new ConcreteObserver( s, "1" ) );
    s.Attach( new ConcreteObserver( s, "2" ) );
    s.Attach( new ConcreteObserver( s, "3" ) );

// Change subject and notify observers
    s.SubjectState = "ABC";
    s.Notify();
  }
}

四、 从Java类库看设计模式

Observer模式的功用,是希望两个(或多个)对象,我们称之为Subject和Observer,当一方的状态发生改变的时候,另一方能够得到通知。也就是说,作为Observer的一方,能够监视到Subject的某个特定的状态变化,并为之做出反应。一个简单的例子就是:当一个用户视图中的数据被用户改变后,后端的数据库能够得到更新,而当数据库被其他方式更新后,用户视图中的数据显示也会随之改变。

fig1

在JDK中实际上有一个对Observer模式的简单的实现:就是类java.util.Observerable和接口java.util.Observer。java.util.Observerable类对应于Subject,而java.util.Observer就是观察者了。JDK中并没有把这两个部分都设计为接口,而是让类java.util.Observerable提供了部分的实现,简化了许多编程的工作。当然,这也减少了一定的灵活性。

下面列出了Observer和Observeral的函数列表,及其简单的功能说明

java.util.Observer:
public void update(Observable obs, Object obj)

java.util.Observer 接口很简单,只定义了这一个方法,狭义的按照Observer模式的说法,Observer应该在这个方法中调用Subject的getXXX()方法来取得最新的状态,而实际上,你可以只是在其中对Subject的某些事件进行响应。这便是Java中的代理事件模型的一个雏形--对事件进行响应。只不过,在Observer模式中将事件特定化为某个状态/数据的改变了。

java.util.Observable

public void addObserver(Observer obs)

向Subject注册一个Observer。也就是把这个Observer对象添加到了一个java.util.Observable内部的列表中。在JDK中对于这个列表是简单的通过一个java.util.Vector类来实现的,而实际上,在一些复杂的Observer模式的应用中,需要把这个部分单另出来形成一个Manager类,来管理Subject和Observer之间的映射。这样,Subject和Observer进一步的被解藕,程序也会具有更大的灵活性。

public void deleteObserver(Observer obs)
从Subject中删除一个已注册了Observer的引用。

public void deleteObservers()
从Subjec中删除所有注册的Observer的引用。

public int countObservers()
返回注册在Subject中的Observer个数。

protected void setChanged()
设置一个内部的标志以指明这个Ovserver的状态已经发生改变。注意这是一个protected方法,也就是说只能在Observer类和其子类中被调用,而在其它的类中是看不到这个方法的。

protected void clearChanged()
清除上叙的内部标志。它在notifyObservers()方法内部被自动的调用,以指明Subject的状态的改变已经传递到Ovserver中了。

public boolean hasChanged()
确定Subject的状态是否发生了改变。

public void notifyObservers(Object obj)
它首先检查那个内部的标志,以判断状态是否改变,如果是的话,它会调用注册在Subject中的每个Observer的update()方法。在JDK中这个方法内部是作为synchronized来实现的,也就是如果发生多个线程同时争用一个java.util.Observerable的notifyObservers()方法的话,他们必须按调度的等待着顺序执行。在某些特殊的情况下,这会有一些潜在的问题:可能在等待的过程中,一个刚刚被加入的Observer会被遗漏没有被通知到,而一个刚刚被删除了的Observer会仍然收到它已经不想要了的通知。

public void notifyObservers()
等价于调用了notifyObservers(null)。

因而在Java中应用Observer就很简单了,需要做的是:让需要被观察的Subject对象继承java.util.Observerable,让需要观察的对象实现java.util.Observer接口,然后用java.util.Observerable的addObserver(Observer obj)方法把Observer注册到Subject对象中。这已经完成了大部分的工作了。然后调用java.util.Observerable的notifyObservers(Object arg)等方法,就可以实现Observer模式的机理。我们来看一个简单使用了这个模式的例子。这个例子有三个类:FrameSubject,DateSubject,FrameObject和EntryClass,FrameSubject中用户可以设置被观察的值,然后自动的会在FrameObject中显示出来,DateSubject封装被观察的值,并且充当Observer模式中的Subject。

public class FrameSubject extends JFrame {
…………..
  //因为无法使用多重继承,这儿就只能使用对象组合的方式来引入一个
//java.util.Observerable对象了。
  DateSubject subject=new DateSubject();
 //这个方法转发添加Observer消息到DateSubject。
  public void registerObserver(java.util.Observer  o){
    subject.addObserver(o);
 }
  //数据改变,事件被触发后调用notifyObservers()来通知Observer。
void jButton1_actionPerformed(ActionEvent e) {
    subject.setWidthInfo(Integer.parseInt(jTextField1.getText()));
    subject.setHeightInfo(Integer.parseInt(jTextField2.getText()));
    subject.notifyObservers();
  }
……………
}
public class DateSubject extends Observable {
    //封装被观察的数据
  private int widthInfo;
  private int heightInfo;
  public int getWidthInfo() {
    return widthInfo;
  }
  public void setWidthInfo(int widthInfo) {
this.widthInfo = widthInfo;
//数据改变后,setChanged()必须被调用,否则notifyObservers()方法会不起作用
this.setChanged();
  }
  public void setHeightInfo(int heightInfo) {
    this.heightInfo = heightInfo;
    this.setChanged();
  }
  public int getHeightInfo() {
    return heightInfo;
  }
}
public class FrameObserver extends JFrame implements java.util.Observer {
…………..
    //观察的数据
   int widthInfo=0;
   int heightInfo=0;
//在update()方法中实现对数据的更新和其它必要的反应。
  public void update(Observable o, Object arg) {
    DateSubject subject=(DateSubject) o;
    widthInfo=subject.getWidthInfo();
    heightInfo=subject.getHeightInfo();
    jLabel1.setText("The heightInfo from subject is:  ");
    jLabel3.setText(String.valueOf(heightInfo));
    jLabel2.setText("The widthInfo from subject is:  ");
    jLabel4.setText(String.valueOf(widthInfo));
  }
…………….
}
public class EntryClass {
    public static void main(String[] args) {
    ……………..
    FrameSubject frame = new FrameSubject();
FrameObserver frame2=new FrameObserver();
//在Subject中注册Observer,将两者联系在一起
frame.registerObserver(frame2);
…………..
frame.setVisible(true);
frame2.setVisible(true);
    ……………..
    }
}

我认为在JDK中这个Observer模式的实现,对于一般的Observer模式的应用,已经是非常的足够了的。但是一方面它用一个类来实现了Subject,另一方面它使用Vector来保存Subject对于Observer的引用,这虽然简化了编程的过程,但会限制它在一些需要更为灵活,复杂的设计中的应用,有时候(虽然这种情况不多),我们还不得不重新编写新的Subject对象和额外的Manager对象来实现更为复杂的Observer模式的应用

 

这一部分主要的讨论了模式的概念。随着现代软件工业的不断进步,软件系统的规模的日益扩大,越来越需要对某些个不断出现的问题进行模式化思维,以成功的经验或者失败的教训来减少软件开发失败的风险。模式代表了一种文档化的经验,它为某一类的问题提供了最好(或者说很好)的解决方案,使得即使不是经验丰富的软件工程师,也能够根据模式来构建相对成功的系统。本节给出的一个Obverser模式的示例,比较好的说明了这一点。Obverser模式主要解决在对象间的状态映射或者镜像的问题。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值