观察者模式

首先声明设计原则:开放-关闭原则,当原代码被修改时,说明设计不够好

依赖倒转原则,让程序依赖抽象,而不是相互

 

又称为发布-订阅(publish/subscribe)模式.

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

适用性:

1.当一个抽象模型有两个方面, 其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。

2.当对一个对象的改变需要同时改变其它对象, 而不知道具体有多少对象有待改变。

3.当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之, 你不希望这些对象是紧密耦合的。

组成

抽象主题(subject)角色

把所有对观察者的引用保存到一个集合,每一个抽象主题可以有任意数量的观察者。抽象主题提供一个借口,可以增加和删除观察者。一般用接口和抽象类来实现抽象主题。

抽象观察者(observer)角色

为具体观察者提供一个更新(update)接口,在得到主题通知时更新自己。

具体主题(concrete subject)角色

在具体主题内部状态发生改变时,给所有登记过的观察者发通知。是抽象主题的子类。

具体观察者(concrete observer)角色

实现抽象观察者角色所有要求的更新接口,以便本身的状态与主题的状态相协调。如果需要,具体的观察者角色可以保存一个指向具体主题角色的引用,来获得主题角色的状态信息。

结构

代码实现

subject

//在此使用了抽象类,虽然没有办法实现多继承,但是由于不同主题所要执行的功能基本相同,抽象类可以省很多代码量。
public abstract class Subject {
    public static List<Observer> observers = new ArrayList<Observer>();
​
    public void registerObserver(Observer o) {
        if(null == o){
            throw new NullPointerException();
        }
        if(!observers.contains(o)){
            observers.add(o);
        }
    }
​
    public void removeObserver(Observer o) {
        if(null == o){
            throw new NullPointerException();
        }
        if(observers.contains(o)){
            observers.remove(o);
        }
​
    }
​
    public void notifyObservers() {
        for (Observer o:observers) {
            o.update();
​
        }
​
    }
​
}
​

ConcreteSubject

public class ConcreteSubject extends Subject {
        public class ConcreteSubject extends Subject {
    private Object state;
​
    public Object getState() {
        return state;
    }
​
    public void setState(Object state) {
        this.state = state;
    }
}   

observer

// 使用接口,由于具体的观察者模式可以是各种各样的类,所以用接口实现
public interface Observer {
    public void update();
}

ConcreteObserver

//未修改版,此时使用ConcreteSubject,使用具体实现主题会使得该具体观察者只能接受一个主题对象通知,因为要用this.subject.getState()获得状态,而抽象类Subject没有该属性和方法。
public class ConcreteObserver implements Observer {
    private ConcreteSubject subject;
    private Object state;
​
    public ConcreteObserver(ConcreteSubject subject){
        this.subject = subject;
        subject.registerObserver(this);
​
    }
​
    @Override
    public void update() {
        state = this.subject.getState();
        System.out.println("i am "+this.hashCode()+"and  receive "+state);
​
    }
}
/**
 *修改版一:接收抽象类subject对象,在update方法进行对象类型判断,然后对subject进行转型,  *就可以实现getState()方法。
*/
public class ConcreteObserver2 implements Observer {
    private Subject subject;
​
    public ConcreteObserver2(Subject subject){
        this.subject = subject;
        subject.registerObserver(this);
    }
    @Override
    public void update() {
        if(subject instanceof ConcreteSubject){
            ConcreteSubject sub = (ConcreteSubject) this.subject;
            Object state = sub.getState();
            System.out.println("i am "+this.hashCode()+"and  receive "+state);
        }
​
    }
}
/**
 * 修改版本三:具体主题实现notify(object org)推信息,
 * 而具体观察者实现update(subject sub,object org)拉去信息
 */
public class ConcreteObserver3 implements Observer {
    private Subject3 subject;
​
    public ConcreteObserver3(Subject3 subject){
        this.subject = subject;
        subject.registerObserver(this);
    }
​
    @Override
    public void update() {
​
    }
​
    @Override
    public void update(Subject3 subject, Object org) {
        System.out.println("i am "+this.hashCode()+"and  receive "+org);
    }
}
//具体实现主题
public class ConcreteSubject3 extends Subject3 {
    private Object state;
​
    public Object getState() {
        return state;
    }
​
    public void setState(Object state) {
        this.state = state;
    }
​
    public void notifyObservers(Object arg) {
        for (Observer o:observers) {
            o.update(this,arg);
​
        }
​
    }
}

测试

public class Client {
    public static void main(String[] args) {
        ConcreteSubject subject = new ConcreteSubject();
        Observer observer1 = new ConcreteObserver(subject);
        Observer observer2 = new ConcreteObserver(subject);
        Observer observer3 = new ConcreteObserver(subject);
        //版本二
        Observer observer4 = new ConcreteObserver2(subject);
        Observer observer5 = new ConcreteObserver2(subject);
        subject.setState("boss is coming!");
        subject.notifyObservers();
        //版本三
        ConcreteSubject3 subject3 = new ConcreteSubject3();
        Observer observer6 = new ConcreteObserver3(subject3);
        Observer observer7= new ConcreteObserver3(subject3);
        subject3.setState("boss is coming!");
        subject3.notifyObservers(subject3.getState());
    }
}

java对观察者模式的内置支持

java.util.Observable类充当观察者模式中的抽象主题角色(在这里可以将其称为“可观察者”)

java.util.Observer接口充当观察者模式中的抽象观察者角色(体现了“松耦合”)

java内置的对观察者模式的支持结构图为:

 

不足之处(摘自HeadFirst):java.util.Observable是一个抽象类,就像我们前面所提到的,因为java是单继承的,这使得某类不可能同时具有Observable和其他超类的行为,这限制了Observable的复用潜力(这也是在代码实现1中使用interface的原因)。另外,Observable将关键方法如setChanged()设置成protected,这意味着,:除非你继承Observable类,否则你无法创建Observable实例并组合到你自己的对象中来,这违反了“多用组合,少用继承”的原则。

 

特点

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

不足与改进

尽管已经使用了依赖倒转原则,但是抽象通知者还是依赖于抽象观察者,也就是说,一旦没有抽象观察者这样的接口,通知功能就不能实现。并且每一个具体的观察者对象不一定要调用更新方法。

解决方式:观察者与通知者互不相识,建立一个委托者来实现通知业务。

委托就是一种引用方法的类型。一旦为委托分配了方法,委托将与该方法具有完全相同的行为。委托方法的使用可以像其他任何方法一样,具有参数和返回值,委托可以看作是对函数的抽象,是函数的‘类’,委托实例将代表一个具体的函数。

委托前提:委托对象所搭载的所有方法必须具有相同的原形和形式,也就是说有相同的参数列表和返回值类型。

委托模式在C#中有实现,在java中可以通过动态代理的方式实现

实现原理:

delegate void EvnetHandler();------声明了一个特殊的类
在具体通知者类中实现 
public event EventHandler Update; //声明EvnetHandler的委托事件,名称为update-----声明了一个类变量
并在notify()方法中调用Update(),//再通知中调用update()
public void notify(){
    Update()
}
​
ConcreteSubject subject = new ConcreteSubject()
ConcreteObserverA a = new ConcreteObserverA() //具体观察者A
ConcreteObserverB b = new ConcreteObserverB() //具体观察者B
​
subject.Update += new EventHandler(a.updateXXX())
subject.Update += new EventHandler(b.updateXXX())
----将a.updateXXX()和b.updateXXX()方法委托给subject.Update
subject.SubjectState = "我来了!"
subject.notify();
​

注:一个委托可以搭载多个方法,所有方法会依次被唤起,并且锁搭载的方法可以不属于同一个类。

补充

1,以上的主题或和观察者中的状态(数据/通知的消息)state泛指主题或者观察者中的状态信息,可以是一组数据,并不是只有一个Object类型的状态数据State.

2,根据自己的需要适当实现观察者模式:抽象主题的实现方式(接口或抽象类?)是否在主题或观察者中加入表示本身状态的属性、是否在观察者中加入一个主题类型的引用(这个引用的类型是抽象主题类型的还是具体主题类型的?)、选择传递数据的方式(推或拉?)以及是否采用类似Observable中setChanged()方法适当调整主题通知观察者的程度(是立即通知还是达到一定程度才通知还是..?)

3,以上是简单依赖关系的观察者模式。

对于复杂依赖关系的观察者模式:(摘抄《设计模式》)

封装复杂的更新语义:

当目标和观察者间的依赖关系特别复杂时, 可能需要一个维护这些关系的对象。我们称这样的对象为更改管理器(ChangeManager)。ChangeManager是一个Mediator(中介者)模式的实例。通常只有一个ChangeManager, 并且它是全局可见的。它的目的是尽量减少观察者反映其目标的状态变化所需的工作量。例如, 如果一个操作涉及到对几个相互依赖的目标进行改动, 就必须保证仅在所有的目标都已更改完毕后,才一次性地通知它们的观察者,而不是每个目标都通知观察者。

ChangeManager有三个责任:

a) 它将一个目标映射到它的观察者并提供一个接口来维护这个映射。这就不需要由目标来维护对其观察者的引用, 反之亦然。-----------------接口 Register(Subject,Observer)

b) 它定义一个特定的更新策略。--------当目标更新全部完成时,才会通知它们的观察者

c) 根据一个目标的请求, 它更新所有依赖于这个目标的观察者。

基于ChangeManager的Observer模式的实现

SimpleChangeManager总是更新每一个目标的所有观察者。

DAGChangeManager处理目标及其观察者之间依赖关系构成的无环有向图。

当一个观察者观察多个目标时, ,两个或更多个目标中产生的改变可能会产生冗余的更新。DAGChangeManager保证观察者仅接收一个更新。当不存在多重更新的问题时, SimpleChangeManager更好一些。

 

参考资料:

1,https://blog.csdn.net/jialinqiang/article/details/8871965 作者:jiajialin

2,《大话数据模式》

 posted on 2018-10-26 15:01  独唱之人  阅读(182)  评论(0编辑  收藏  举报