观察者模式

在讲述这个模式之前,我们先看一个案例:前台向同事通知“老板来了”

//前台秘书类
public class Secretary {
    private List<StockObserver> observers = new ArrayList<>();
    private String action;
    
    //增加同事,有几个同事请前台帮忙,于是就给集合增加几个对象
    public void attach(StockObserver observer){
        observers.add(observer);
    }
    
    //通知同事,待老板来时,就给所有登记的同事发通知“老板来了”
    public void notice(){
        for (StockObserver stockObserver : observers) {
            stockObserver.update();
        }
    }

    public String getAction() {
        return action;
    }
    //前台通过电话,所说的话或所做的事
    public void setAction(String action) {
        this.action = action;
    }
}

//看股票同事类
public class StockObserver {
    private String name;
    private Secretary sub;
    public StockObserver(String name, Secretary sub) {
        super();
        this.name = name;
        this.sub = sub;
    }
    //得到前台的通知,赶快采取行动
    public void update(){
        System.out.println(sub.getAction()+","+this.name+"关闭股票行情,继续工作");
    }
}

//测试方法
public class Test {
    public static void main(String[] args) {
        //前台小姐姐
        Secretary qiantai = new Secretary();
        //看股票的同事
        StockObserver tongshi1 = new StockObserver("张三", qiantai);
        StockObserver tongshi2 = new StockObserver("李四", qiantai);
        
        //前台记下了两位同事
        qiantai.attach(tongshi1);
        qiantai.attach(tongshi2);
        //发现老板回来
        qiantai.setAction("老板回来了");
        //通知两位同事
        qiantai.notice();
    }
}

输出结果:

老板回来了,张三关闭股票行情,继续工作
老板回来了,李四关闭股票行情,继续工作

这样做的弊端就是耦合度太高:前台类要增加同事类,同时类需要前台的状态,他们之间相互耦合。

现在需要实现解耦,该怎么实现?本着依赖倒转原则,我们给前台类和同事类分别增加一个抽象类(或接口),利用多态,在原来引用实现类的地方都改成引用抽象类(或接口)

对同事类的修改:

//给同事类增加一个抽象类,让具体的同事类去实现
public abstract class Observer {
    protected String name;
    protected Subject sub;
    public Observer(String name, Subject sub) {
        this.name = name;
        this.sub = sub;
    }
    //抽象方法
    public abstract void update();
}

//看股票行情的同事类
public class StockObserver extends Observer{
    //原本第二个参数是前台类,现在第二个参数是一个抽象类,前台类继承这个抽象类
    public StockObserver(String name, Subject sub) {
        super(name, sub);
    }

    //得到前台的通知,赶快采取行动
    public void update(){
        System.out.println(sub.getAction()+","+this.name+"关闭股票行情,继续工作");
    }
}   
 
//看NBA直播的同事类
public class NBAObserver extends Observer{
    //原本第二个参数是前台类,现在第二个参数是一个抽象类,前台类继承这个抽象类    
    public NBAObserver(String name, Subject sub) {
        super(name, sub);
    }

    //得到前台的通知,赶快采取行动
    public void update(){
        System.out.println(sub.getAction()+","+this.name+"关闭NBA直播,继续工作");
    }
}            

对前台类的修改:

//新增一个抽象类,前台类继承这个抽象类
public abstract class Subject {
    public abstract void attach(Observer observer);
    public abstract void notice();
    public abstract String getAction();
    public abstract void setAction(String action);
}

//前台类继承抽象类,把原先引用具体同事类的地方改成引用同事类的父类Observer
public class Secretary extends Subject{
    private List<Observer> observers = new ArrayList<>();
    private String action;
    
    //增加同事,有几个同事请前台帮忙,于是就给集合增加几个对象
    @Override
    public void attach(Observer observer){
        observers.add(observer);
    }
    
    //通知同事,待老板来时,就给所有登记的同事发通知“老板来了”
    @Override
    public void notice(){
        for (Observer stockObserver : observers) {
            stockObserver.update();
        }
    }
    @Override
    public String getAction() {
        return action;
    }
    //前台通过电话,所说的话或所做的事
    @Override
    public void setAction(String action) {
        this.action = action;
    }
}

//会计类继承抽象类,把原先引用具体同事类的地方改成引用同事类的父类Observer
public class Bursar extends Subject{
    private List<Observer> observers = new ArrayList<>();
    private String action;
    
    //增加同事,有几个同事请前台帮忙,于是就给集合增加几个对象
    public void attach(Observer observer){
        observers.add(observer);
    }
    
    //通知同事,待老板来时,就给所有登记的同事发通知“老板来了”
    public void notice(){
        for (Observer stockObserver : observers) {
            stockObserver.update();
        }
    }

    public String getAction() {
        return action;
    }
    //前台通过电话,所说的话或所做的事
    public void setAction(String action) {
        this.action = action;
    }
}

测试方法

public class Test {
    public static void main(String[] args) {
        //前台小姐姐
        Subject qiantai = new Secretary();
        //会计小姐姐
        Subject bur = new Bursar();
        //看股票的同事
        Observer tongshi1 = new StockObserver("张三", qiantai);
        //看NBA直播的同事
        Observer tongshi2 = new NBAObserver("李四", qiantai);
        
        //前台记下一位同事
        qiantai.attach(tongshi1);
        //会计记下一位同事
        bur.attach(tongshi2);
        //发现老板回来
        qiantai.setAction("老板回来了");
        bur.setAction("老板回来了");
        //通知两位同事
        qiantai.notice();
        bur.notice();
    }
}

输出结果:

老板回来了,张三关闭股票行情,继续工作
老板回来了,李四关闭NBA直播,继续工作

看到这里,肯定有个疑问,这个例子跟观察者模式有啥关系?谁观察了谁?

下面介绍观察者模式(发布-订阅模式):http://www.runoob.com/design-pattern/observer-pattern.html

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

上面的前台通知同事就是一个很好的例子,同事类继承Observer,前台类继承Subject

Subject类可以翻译为主题或抽象通知者,一般用一个抽象类或者一个借口实现。它把所有对观察者对象的引用保存在一个集合里,每个主题都可以有任意数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。实现该类的子类叫做具体主题或具体通知者,将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。

Observer类,抽象观察者,为所有的具体观察者定义一个接口,在得到主题的通知时更新自己。这个接口叫做更新接口。抽象观察者一般用一个抽象类或者一个接口实现。更新接口通常包含一个update()方法,这个方法叫做更新方法。实现该类的子类叫做具体观察者,实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。具体观察者角色可以保存一个指向具体主题对象的引用。

用观察者模式的动机是什么呢?

将一个系统分割成一系列相互协作的类有一个很不好的副作用,那就是需要维护相关对象间的一致性。我们不希望为了维持一致性而使各类紧密耦合,这祥会给维护、扩展和重用都带来不便。而观察者模式的关键对象是主题Subject和观察者Observer. 一个Subject可以有任意数目的依赖它的Observer,一旦Subject的状态发生了改变,所有的Observer都可以得到通知。Subject发出通知时并不需要知道谁是它的观察者,也就是说,具体观察者是谁,它根本不需要知道。而任何一个具体观察者不知道也不需要知道其他观察者的存在 

什么时候考虑使用观察者模式呢?
当一个对象的改变需要同时改变其他对象,且它不知道具体有多少对象有待改变时,应该考虑使用观察者模式。当一个抽象模型有两个方面,其中一方面依赖于另一方面,这时用观察者模式可以将这两者封装在独立的对象中使它们各自独立地改变和复用。观察者模式所做的工作其实就是在解除耦合。让耦合的双方都依赖于抽象。而不是依赖于具体,从而使得各自的变化都不会影响另一边的变化。

原文地址:https://www.cnblogs.com/jwen1994/p/10120640.html