【设计模式】观察者模式

一、前言

  前面学习了策略模式,接着学习观察者模式,观察者模式是一个很常用的模式,如订阅RSS这个功能就适合使用观察者模式来实现,园友订阅了博客园文章后,当博客园的文章有更新时,会收到相应的通知,这就是观察者模式的应用,并且JDK中都内置了对观察者模式的支持,下面来学习观察者模式。

二、观察者模式定义

  定义:观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。

  定义中的一通常称为主题,多称为观察者,当主题发生变化时,观察者会收到相应的变化,下面通过示例来演示观察者模式。

三、示例

  若有这样的一个场景,当园友订阅博客园文章后,博客园文章更新后,程序员需要收到更新的文章。这时,使用观察者模式最合适不过了。下面是类图

  说明:Subject是主题接口,包含了三个方法,CNBlog是具体的主题,表示博客园,Oberser是观察者接口,包含了update方法,在主题发生变化时,update方法会被调用,Coder是具体的观察者,表示程序员。

  3.1 v1.0

  根据类图,代码如下

  Subject

package com.hust.grid.leesf.observer;

public interface Subject {
    void registerObserver(Observer observer);
    void removeObserver(Observer observer);
    void notifyObservers();
}
View Code

  Observer

package com.hust.grid.leesf.observer;

public interface Observer {
    void update(String message);
}
View Code

  CNBlog  

package com.hust.grid.leesf.observer;

import java.util.ArrayList;
import java.util.List;

public class CNBlog implements Subject {
    private List<Observer> observers;
    private String message;
    
    public CNBlog() {
         observers = new ArrayList<Observer>();
    }
    
    @Override 
    public void registerObserver(Observer observer) {
        observers.add(observer);
    } 
    
    @Override
    public void removeObserver(Observer observer) {
        int index = observers.indexOf(observer);
        if (index >= 0) {
            observers.remove(index);
        }
    }
    
    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(message);
        }
    }
    
    /**
     * 模拟博客园的文章更新了
     * @param message
     */
    public void setMessage(String message) {
        this.message = message;
        // 有更新
        messageChanged();
    }
    
    private void messageChanged() {
        // 通知观察者
        notifyObservers();
    }
}
View Code

  Coder  

package com.hust.grid.leesf.observer;

public class Coder implements Observer {
    private String name;
    public Coder(String name) {
        this.name = name;
    }
    @Override
    public void update(String message) {
        System.out.println("I am " + name + ", received updated message from subject is [" + message + "]");
    }
}
View Code

  Main(用于测试)

package com.hust.grid.leesf.observer;

/**
 * @since 2016/6/1
 * @author LEESF
 *
 */
public class Main {
    public static void main(String[] args) {
        // 新生主题
        CNBlog cnBlog = new CNBlog();
        // 新生程序员
        Observer leesf = new Coder("leesf");
        // 向主题注册
        cnBlog.registerObserver(leesf);
        // 新生程序员
        Observer lee = new Coder("lee");
        // 向主题注册
        cnBlog.registerObserver(lee);
        
        // 模拟主题发生变化
        cnBlog.setMessage("leesf发布了一篇新文章");
        System.out.println("----------------------------------------");
        // 移除观察者
        cnBlog.removeObserver(lee);
        
        // 模拟主题发生变化
        cnBlog.setMessage("leesf又发布了一篇新文章");
    }
}
View Code

  运行结果

I am leesf, received updated message from subject is [leesf发布了一篇新文章]
I am lee, received updated message from subject is [leesf发布了一篇新文章]
----------------------------------------
I am leesf, received updated message from subject is [leesf又发布了一篇新文章]

  说明:首先,leesf与lee向CNBlog主题进行了注册,成为观察者,之后,CNBlog的信息发生变化,可以看到,此时,leesf与lee都收到了通知;之后,从主题中移除观察者lee,CNBlog的信息再次发生变化时,只有leesf收到了信息,而lee则不会收到信息。

  这样的设计会使得系统极具扩展性,可以方便的添加其他主题或者观察者而不会影响之前的代码。如,可以添加一个CSDN主题、今日头条主题等,这些主题只需要实现Subject接口即可,同时,也可以添加其他类型观察者,如设计师、科学家等,这些观察者只需要实现Observer接口即可。类图如下图所示

  说明:Coder向CNBlog、CSDN主题进行注册,成为它们观察者;Designer向TouTiao、CSDN主题进行注册,成为它们的观察者;Scientist想TouTiao注册,成为它的观察者。

  3.2 v2.0

  根据类图,代码如下

  CSDN 

package com.hust.grid.leesf.observer;

import java.util.ArrayList;
import java.util.List;

public class CSDN implements Subject {
    private List<Observer> observers;
    private String message;
    
    public CSDN() {
        observers = new ArrayList<Observer>();
    }
    
    @Override 
    public void registerObserver(Observer observer) {
        observers.add(observer);
    } 
    
    @Override
    public void removeObserver(Observer observer) {
        int index = observers.indexOf(observer);
        if (index >= 0) {
            observers.remove(index);
        }
    }
    
    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(message);
        }
    }
    
    /**
     * 模拟CSDN的文章更新了
     * @param message
     */
    public void setMessage(String message) {
        this.message = message;
        // 有更新
        messageChanged();
    }
    
    private void messageChanged() {
        // 通知观察者
        notifyObservers();
    }    
}
View Code

  TouTiao

package com.hust.grid.leesf.observer;

import java.util.ArrayList;
import java.util.List;

public class TouTiao implements Subject {
    private List<Observer> observers;
    private String message;
    
    public TouTiao() {
        observers = new ArrayList<Observer>();
    }
    
    @Override 
    public void registerObserver(Observer observer) {
        observers.add(observer);
    } 
    
    @Override
    public void removeObserver(Observer observer) {
        int index = observers.indexOf(observer);
        if (index >= 0) {
            observers.remove(index);
        }
    }
    
    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(message);
        }
    }
    
    /**
     * 模拟头条的的新闻更新了
     * @param message
     */
    public void setMessage(String message) {
        this.message = message;
        // 有更新
        messageChanged();
    }
    
    private void messageChanged() {
        // 通知观察者
        notifyObservers();
    }    
}
View Code

  Designer 

package com.hust.grid.leesf.observer;

public class Designer implements Observer {
    private String name;
    public Designer(String name) {
        this.name = name;
    }
    @Override
    public void update(String message) {
        System.out.println("I am " + name + ", received updated message from subject is [" + message + "]");
    }
}
View Code

  Scientist

package com.hust.grid.leesf.observer;

public class Scientist implements Observer {
    private String name;
    public Scientist(String name) {
        this.name = name;
    }
    @Override
    public void update(String message) {
        System.out.println("I am " + name + ", received updated message from subject is [" + message + "]");
    }
}
View Code

  Main(用作测试)

package com.hust.grid.leesf.observer;

/**
 * @since 2016/6/1
 * @author LEESF
 *
 */
public class Main {
    public static void main(String[] args) {
        // 新生主题
        CNBlog cnBlog = new CNBlog();
        CSDN csdn = new CSDN();
        TouTiao touTiao = new TouTiao();
        
        // 新生程序员
        Observer leesf = new Coder("leesf");
        // 向CNBlog主题注册
        cnBlog.registerObserver(leesf);
        // 向CSDN主题注册
        csdn.registerObserver(leesf);
        
        // 新生设计师
        Observer dyd = new Coder("dyd");
        // 向CSDN主题注册
        csdn.registerObserver(dyd);
        // 向头条主题注册
        touTiao.registerObserver(dyd);
        
        // 新生科学家
        Observer ld = new Scientist("ld");
        // 向头条主题注册
        touTiao.registerObserver(ld);
        
        // 模拟主题发生变化
        cnBlog.setMessage("leesf在博客园上发布了一篇新文章");
        System.out.println("----------------------------------------");
        
        // 模拟主题发生变化
        csdn.setMessage("leesf在CSDN上发布了一篇新文章");
        System.out.println("----------------------------------------");
        
        // 模拟主题发生变化
        touTiao.setMessage("头条更新了新闻");
    }
}
View Code

  运行结果

I am leesf, received updated message from subject is [leesf在博客园上发布了一篇新文章]
----------------------------------------
I am leesf, received updated message from subject is [leesf在CSDN上发布了一篇新文章]
I am dyd, received updated message from subject is [leesf在CSDN上发布了一篇新文章]
----------------------------------------
I am dyd, received updated message from subject is [头条更新了新闻]
I am ld, received updated message from subject is [头条更新了新闻]

  说明:可以看到,不同的主题更新,会通知向该主题注册的观察者。并且新增的主题和观察者并没有修改原来的Subject、Observer代码,使得其具有很好的可维护性。

  有了观察者的概念,再看JDK里面的观察者模式就会非常简单,有兴趣的读者可以自行查阅。笔者不再累赘。

四、总结

  掌握了观察者模式后,在进行系统设计的时候,若可以使用观察者模式,则尽管去使用观察者模式吧,它会让你的系统更加灵活和易于扩展。所有源代码已经上传至github,欢迎fork,谢谢各位园友的观看~

posted @ 2016-06-02 08:15  leesf  阅读(1020)  评论(0编辑  收藏  举报