讲故事,学(AHK)设计模式—观察者模式

讲故事,学(AHK)设计模式—观察者模式

文章内容完全抄自 讲故事,学(AHK)设计模式—观察者模式,对文章中AutoHotkey中内容进行了测试,C++及JAVA中的内容未做测试。

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

观察者模式概述

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

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

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

观察者模式定义

对象之间的一种一对多的依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆能得到通知并被自动更新。简而言之,观察者模式=发布者(publisher)+订阅者(subscriber)。
我们很多人都订过杂志,其过程很简单。只要告诉邮局我们所要订的杂志名、投递的地址,付了钱就OK。出版社定期会将出版的杂志交给邮局,邮局会根据订阅的列表,将杂志送达消费者手中。这样我们就可以看到每一期精彩的杂志了。

仔细思考一下订杂志的过程,我们会发现这样几个特点:

  1. 消费者订杂志不需要直接找出版社;
  2. 出版社只需要把杂志交给邮局;
  3. 邮局将杂志送达消费者。

邮局在整个过程中扮演了非常重要的中转作用,在出版社和消费者相互不需要知道对方的情况下,邮局完成了杂志的投递。

我们再进一步抽象,用户业务就是消息的"生产者",它将消息发布到消息管理器。邮件业务就是 消息的"消费者",它将收到的消息进行处理。邮局可以订阅很多种杂志,杂志都是通过某种编号来区分;消息管理器也可以管理多种消息,每种消息都会有一个 "主题"来区分,消费者都是通过主题来订阅的。

20181124103928966

讲故事,学设计模式

下面是一个猎头的典型例子。

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

AutoHotkey代码

;~ --------------------- 
;~ 作者:sunwind
;~ 来源:CSDN 
;~ 原文:https://blog.csdn.net/liuyukuan/article/details/84428817
;~ 版权声明:本文为博主原创文章,转载请附上博文链接!

main:
    hunter:=new Hunter()

    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

class Observer
{
    name:=""
    __New(n)
    {	
        this.name:=n
    }
    getName(){
        return this.name
    }
    update(subject){
    }
}

class JobSeeker extends Observer
{
    update(subject)
    {
        Print(this.getName() "得到猎头通知!")
        subject.getJobs() ;写错函数名也不报错
    }
}

class Subject
{
    observerList:={}
    __New()
    {	
        this.observerList:={}
    }
    registerObserver(observer){
        Print(observer.getName() "向猎头注册")
        this.observerList.push(observer)
    }
    removeObserver(observer){
        for k,v in this.observerList
        {
            ; Print(v.getName() "`n->" observer.getName())
            if (v.getName()=observer.getName()){
                Print( observer.getName() "取消向猎头注册")
                this.observerList.Remove(v)
                return
            }
        }
        Print(observer.getName() "没有向猎头注册")
    }
    notifyAllObserver()
    {
        MsgBox
    }
}

class Hunter extends Subject
{
    jobs:={}
    __New()
    {	
        this.jobs:={}
    }
    ;遍历观察者集合,调用每一个观察者的响应方法
    notifyAllObserver()
    {
        for k,v in this.observerList
        {
            v.update(this)
        }
    }
    ;添加工作
    addJob(jobName)
    {
        this.jobs.push(jobName)
        ;;观察目标发生变化,通知所有观察者
        this.notifyAllObserver()
    }
    ;获取工作列表
    getJobs(){
        ;~ MsgBox 可提供的工作最新列表
        print("可提供的工作最新列表:")
        for k,v in this.jobs
        {
            out.=v "`r`n" 
        }
        Print(out)
    }
}

Print(内容){
    if(WinExist("ahk_class Notepad")=0)
    {
        Run,Notepad.exe
        sleep 500
    }
    ;~ ControlSendRaw,,%内容%`r,ahk_class Notepad
    Control, EditPaste, %内容%`r`n, Edit1, ahk_class Notepad
}

ahk代码运行结果

20210928163049006

C++代码

#include <list>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
 
//class Observer;
//Observer接口:抽象观察者
class Observer{
private:
	string name;
public:
	Observer(string n):name(n){
	}
	string getName(){
		return name;
	}
	virtual void update(Subject* s)=0;
};
 

//class Subject;
//Subject接口:抽象主题
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;
}

C++程序运行结果:

JAVA代码

//Subject接口
public interface Subject {
    public void registerObserver(Observer o);
    public void removeObserver(Observer o);
    public void notifyAllObservers();
}


//Observer接口
public interface Observer {
    public void update(Subject s);
}

//Hunter类实现了Subject接口
import java.util.ArrayList;
public class HeadHunter implements Subject{

//define a list of users, such as Mike, Bill, etc.
private ArrayList<Observer> userList;
private ArrayList<String> jobs;

public HeadHunter(){
    userList = new ArrayList<Observer>();
    jobs = new ArrayList<String>();
}

@Override
public void registerObserver(Observer o) {
    userList.add(o);
}

@Override
public void removeObserver(Observer o) {}

@Override
public void notifyAllObservers() {
    for(Observer o: userList){
        o.update(this);
    }
}

public void addJob(String job) {
    this.jobs.add(job);
    notifyAllObservers();
}

public ArrayList<String> getJobs() {
    return jobs;
}

public String toString(){
    return jobs.toString();
}
}

//JobSeeker是一个观察者:
public class JobSeeker implements Observer {
    private String name;
    public JobSeeker(String name){
        this.name = name;
    }
    @Override
    public void update(Subject s) {
        System.out.println(this.name + " got notified!");
        //print job list
        System.out.println(s);
    }
}


//开始使用:
public class Main {
    public static void main(String[] args) {
        HeadHunter hh = new HeadHunter();
        hh.registerObserver(new JobSeeker("Mike"));
        hh.registerObserver(new JobSeeker("Chris"));
        hh.registerObserver(new JobSeeker("Jeff"));
 
    //每次添加一个个job,所有找工作人都可以得到通知。
    hh.addJob("Google Job");
    hh.addJob("Yahoo Job");
}
}

观察者模式总结

主要优点

  1. 观察者模式可以实现表示层和数据逻辑层的分离。
  2. 观察者模式在观察目标和观察者之间建立了一个抽象的耦合。
  3. 观察者模式支持广播通信,观察目标向所有已注册的观察者对象发送通知,简化一对多系统设计的难度。

主要缺点

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

适用场景

  1. 一个抽象模型有两个方面,其中一个方面依赖另一个方面。
  2. 一个对象的改变将导致一个或多个其它对象发生改变,而不知道具体有多少个对象发生改变。

扩展

上面的观察者模式实现起来虽然简单,但是当多个对象相互观察,就会使得相互之间的关系错综复杂,产生过渡耦合。

引入中介者

如果是比较大的项目肯定是不能这么做的。
当引入中介者模式后,对象之间的关系由网状改为星状。

20170718174814383

在观察者模式中引入一个中介者,作为事件处理的中心——EventCenter,所有事件的派发、订阅都是通过这个中心进行的。

posted @ 2021-09-28 16:43  生命在等待中延续  阅读(148)  评论(0编辑  收藏  举报