对象间的联动--观察者模式

随着交通信号灯的变化,汽车的行为也将随之变化,一盏交通信号灯可以指挥多辆汽车。

在软件系统中,有些对象之间也存在类似交通信号灯和汽车之间的关系,一个对象状态或者行为的变化将导致其他对象的状态或者行为也发生变化,它们之间将产生联动。为了更好的描述对象之间存在的这种一对多(包括一对一)的联动,观察者模式应运而生,它定义对象之间一对多的依赖关系,让一个对象的改变能够影响其它对象。

观察者模式概述

观察者模式用于建立一种对象之间的依赖关系,一个对象发生改变时将通知其它对象,其它对象将相应作出反应。在观察者模式中,发生改变的对象称为观察目标,而被通知的对象称为观察者,一个观察目标可以对应多个观察者,而且这些观察者之间没有任何相互联系,可以根据需要增加或者删除观察者,使得系统更易于扩展。

观察者模式定义

定义对象之间的一种一对多的依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆能得到通知并被自动更新。观察者模式的别名包括“发布--订阅”模式。

观察者模式结构图

观察者模式结构图中包含以下4个角色:

(1) 抽象主题角色:把所有对观察者对象的引用保存在一个集合(std::list)中,每个抽象主题角色都可以有任意数量的观察者。抽象主题提供一个接口,可以增加和删除观察者角色。一般用一个抽象类和接口来实现。

(2) 具体主题角色:在具体主题内部状态改变时,给所有登记过的观察者发出通知。具体主题角色通常用一个子类实现。

(3) 抽象观察者角色:为所有具体的观察者定义一个接口,在得到主题的通知时更新自己。

(4) 具体观察者角色:该角色实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。通常用一个子类实现。如果需要,具体观察者角色可以保存一个指向具体主题角色的引用。

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

JDK对观察者模式的支持

观察者模式在java语言中的地位非常重要。在JDK的java.util包中,提供了Obsevable类(观察目标)以及Observer(观察者)接口,它们构成了JDK对观察者模式的支持。

如下图所示:

观察者模式与Java事件处理

Java GUI经典示例

观察者模式与MVC

观察者模式总结

主要优点

(1) 观察者模式可以实现表示层和数据逻辑层的分离。

(2) 观察者模式在观察目标和观察者之间建立了一个抽象的耦合。

(3) 观察者模式支持广播通信,观察目标向所有已注册的观察者对象发送通知,简化一对多系统设计的难度。

主要缺点

(1) 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎样发生变化的,而仅仅是知道观察目标发生了变化。

适用场景

(1) 一个抽象模型有两个方面,其中一个方面依赖另一个方面。

(2) 一个对象的改变将导致一个或多个其它对象发生改变,而不知道具体有多少个对象发生改变。

观察者模式应用

简而言之,观察者模式=发布者+注册者。

观察者模式用来对GUI中的动作做侦听。Swing GUI的例子就表明了动作侦听是怎样实现观察者模式的。

下面是一个猎头的典型例子。这个图中有2个角色-猎头和求职者。求职者先在猎头处注册,当有新的工作机会时猎头就会通知求职者。

示例代码如下:

#include <list>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;

class Subject;
//Observer接口:抽象观察者
class Observer{
private:
	string name;
public:
	Observer(string n):name(n){
	}
	string getName(){
		return name;
	}
	virtual void update(Subject* s)=0;
};

//Subject接口:抽象主题
class Observer;
class Subject{
protected:
	//定义一个观察者集合用于存储所有观察者对象
	list<Observer*> observerList;
public:
	//注册方法,用于向观察者集合增加一个观察者
	virtual void registerObserver(Observer* observer){
		cout<<observer->getName()<<"向猎头注册"<<endl;
		observerList.push_back(observer);
	};
	//注销方法,用于在观察者集合中删除一个观察者
	virtual void removeObserver(Observer* observer){
		list<Observer*>::iterator iter=find(observerList.begin(),observerList.end(),observer);
		if(iter==observerList.end()){
			cout<<observer->getName()<<"没有向猎头注册"<<endl;
		}else{
			cout<<(*iter)->getName()<<"取消向猎头注册"<<endl;
			observerList.erase(iter);
		}
		return;
	}
	//声明抽象通知方法
	virtual void notifyAllObserver()=0;
};

//Hunter类:具体主题
class Hunter : public Subject{
private:
	list<string> jobs;
public:
	Hunter(){}

	//实现通知方法
	void notifyAllObserver(){
		list<Observer*>::iterator iter=observerList.begin();
		//遍历观察者集合,调用每一个观察者的响应方法
		while(iter!=observerList.end()){
			(*iter)->update(this);
			++iter;
		}
	}

	//添加工作
	void addJob(string jobName){
		jobs.push_back(jobName);
		//观察目标发生变化,通知所有观察者
		notifyAllObserver();
	}

	//得到工作列表
	void getJobs(){
		list<string>::const_iterator iter=jobs.begin();
		cout<<"可提供的工作最新列表:"<<endl;
		while(iter!=jobs.end()){
			cout<<*iter<<endl;
			++iter;
		}
	}
};

//JobSeeker:具体观察者
class JobSeeker : public Observer{
public:
	JobSeeker(string name):Observer(name){
	}
	void update(Subject *s){
		cout<<getName()<<"得到猎头通知!"<<endl;
		((Hunter*)s)->getJobs();//此处没有针对抽象编程
	}
};

//客户端测试代码
int main(){
	Hunter *hunter=new Hunter();
	Observer *observer1,*observer2;
	observer1=new JobSeeker("Mike");
	observer2=new JobSeeker("Jeff");
	hunter->registerObserver(observer1);
	hunter->registerObserver(observer2);
	hunter->addJob("Google Job");
	hunter->addJob("Yahoo Job");
	hunter->removeObserver(observer1);
	return 0;
}

程序运行结果:

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-30 08:26:33

对象间的联动--观察者模式的相关文章

依赖注入与对象间关系

依赖注入(DI)是控制反转(IoC)的一种方式.目前,在.NET和Java领域已经有相当多基于DI思想的对象容器,如:Spring,Unity等.本文试图避免重复性地介绍DI基础知识和DI容器的使用,而是希望深一层探讨DI的本质和对象间关系,以达到合理设计避免滥用DI的目的. 依赖注入 vs 创建对象 有不少地方这样描述:"依赖注入改变了使用对象前先创建的传统方式,而是从外部注入依赖的对象".这样的描述其实似是而非,先来看一个例子: interface ICar{     void R

第23条:通过委托与数据源协议进行对象间通信

第4章:协议与分类 Objective-C 语言有一项特性叫做"协议"(protocol),它与 Jave 的 "接口"(interface)类似.Objective-C 不支持多重继承,因而我们把某个类应该实现的一系列方法定义在协议里面.协议最为常见的用途是实现委托模式,不过也有其他用法.理解并善用协议可令代码变得更易维护,因为协议这种方式能很好的描述接口. "分类"(category)也是 Objective-C 的一项重要语言特性.利用分类

第16课 Qt对象间的父子关系

1. Qt对象间的关系 (1)Qt对象间可以存在父子关系 ①每一个对象都保存有它所有子对象的指针 ②每一个对象都有一个指向其父对象的指针 (2)当指定Qt对象的父对象时 ①其父对象会在子对象链表中加入该对象的指针 ②该对象会保存指向其父对象的指针 (3)当Qt对象被销毁时 ①将自己从父对象的Children List移除 ②将自己的Children List中的所有对象销毁 ▲使用Qt开发时,不仅要时刻注意内存泄露的问题,还要时刻关注对象是否可能被多次销毁的问题. [编程实验]对象间的父子关系

13. 查看数据库对象间的依赖关系

在SQL Server中,(可编程)对象间的引用即依赖关系,有多种方式可以检查,随着版本变更,方式也有所不同. 父子关系的对象,不通过依赖关系来查询,比如: 1. 外键关系 use tempdb GO --drop table tb1,tb2 create table tb1 ( col1 int Primary key, col2 int ) insert into tb1 values (2,2),(3,2),(4,2),(5,2) GO create table tb2 ( col3 in

第23条:通过委托与数据源协议进行对象间通信

Objective-C语言特性:协议(protocol),它与Java的“接口”(interface)类似. Objective-C不支持多继承,但可以把类的实现方法定义在协议里面. 协议最为常见的用途是实现委托模式(Delegate pattern). 委托模式(Delegate pattern): 用来实现对象间的通信, 该模式的主旨是:定义一套接口,某对象若想授受另一个对象的委托,则需遵从此接口,以便成为其“委托对象”(delegate).而这“另一对象”则可以给其委托对象回传一些消息,也

让两个对象间建立weak关系

这是为了给两个对象间建立weak关系,当一个对象被释放时,另外一个对象再获取这个值时就是nil,也就是不持有这个对象:) 源码: WeakRelatedDictionary.h 与 WeakRelatedDictionary.m // // WeakRelatedDictionary.h // TestDemo // // Created by YouXianMing on 14-9-25. // Copyright (c) 2014年 YouXianMing. All rights reser

对象间引用赋值及方法时引用传递

考虑下面代码: class Program { static void Main(string[] args) { C c1 = new C(); c1.str = "hello"; C c2 = new C(); c2 = c1; //对象名即是对象引用,对象间赋值即是引用复制赋值,此时栈区的c2和c1同时指向堆区的同一块内存区域 Console.WriteLine(c1.str); Console.WriteLine(c2.str); c2.str = "world&qu

iOS开发————对象间通信之block

一.block的概念: 别称:代码段,块,闭包,是苹果公司添加到OC语言中的. 作用:在程序运行的过程中保存一段代码,并且这段代码可以进行传递. 应用:用于对象间的通信. 二.block的语法: 和函数指针的语法相似 要设定block的返回值和参数个数及类型. (1)定义: 无参无返回值的block变量:void (^myBlock)(void) 有参数有返回值的block变量 int (^sumBlock)(int, int); (2)赋值: myBlock = ^{ //block中的代码

7.QT-Qt对象间的父子关系

Qt对象之间可以存在父子关系 继承于QObject类或者其子类的对象,都称为Qt对象 当指定Qt对象的父对象时 需要通过setParent()成员函数来设置对象间的父子关系 子对象将会把自己的指针地址保存在父对象的children List链表里(因为父对象可以拥有多个子对象) 子对象内部会有一个指向其父对象的指针,可以通过parent()成员函数查看 代码试验 需要用到的函数: void QObject::setParent ( QObject * parent ); //设置为parent对