观察者模式介绍

观察者模式又被叫做发布订阅模式。观察者模式在Java中非常的常见,比如说Spring中的事件监听设计就是观察者模式,再比如说Redis、kafka的发布订阅功能就是观察者模式。

应用场景

观察者模式在应用上非常广泛,比如订阅推送,发布订阅功能,聊天场景,微信公众号订阅等非常多。这个模式说白了就是:被观察者(发布者)生产内容,观察者(订阅者)获取内容。比如说订阅推送,假如我的博客发了一篇新的文章,那么那些关注我的人就会收到推送的消息。再比如说51CTO的付费专栏,如果专栏作者发布了新的专栏内容,那么那些订阅了该作者该专栏的人就会收到专栏推送的消息。而聊天分为一对一和一对多,一对一是私聊模式,而一对多是群聊模式,他们都是观察者模式。

观察者模式类图

实现订阅专栏功能

1.Observer接口

/**
 * @ClassName Observer
 * @Description TODO:描述该接口职责
 * @Author ckmike
 * @Date 18-12-7 下午3:00
 * @Version 1.0
 * @Copyright ckmike
 **/
public interface Observer {
    void update(String message);
}

2.Observerable接口

/**
 * @ClassName Observerable
 * @Description TODO:描述该接口职责
 * @Author ckmike
 * @Date 18-12-7 下午2:56
 * @Version 1.0
 * @Copyright ckmike
 **/
public interface Observerable {

    // 注册观察者
    void register(Observer observer);

    // 注销观察者
    void remove(Observer observer);

    void notifyObserver();
}

3.SubscriptionChannel类

/**
 * SubscriptionChannel 简要描述
 * <p> TODO:描述该类职责 </p>
 *
 * @author ckmike
 * @version 1.0
 * @date 18-12-7 下午3:04
 * @copyright ckmike
 **/
public class SubscriptionChannel implements Observerable{

    List<Observer> observers;
    private String message;

    public SubscriptionChannel() {
        this.observers = new ArrayList<Observer>();
    }

    @Override
    public void register(Observer observer) {
        this.observers.add(observer);
    }

    @Override
    public void remove(Observer observer) {
        this.observers.remove(observer);
    }

    @Override
    public void notifyObserver() {
        // 通知所有observer
        for(Observer observer: this.observers){
            observer.update(message);
        }
    }

    public void sendMessage(String message) {
        this.message = message;
        System.out.println("专栏作者更新专栏文章......");
        // 调用通知所有订阅者
        notifyObserver();
    }
}

4.User类

/**
 * User 简要描述
 * <p> TODO:描述该类职责 </p>
 *
 * @author ckmike
 * @version 1.0
 * @date 18-12-7 下午3:13
 * @copyright ckmike
 **/
public class User implements Observer {
    private String name;
    private String message;

    public User(String name) {
        this.name = name;
    }

    @Override
    public void update(String message) {
        this.message = message;
        // 这里应该写到数据库中的,但我们这里就直接输出吧,方便
        read();
    }

    public void read() {
        System.out.println(name+"收到推送消息:"+this.message);
    }
}

5.测试用例

/**
 * Test 简要描述
 * <p> TODO:描述该类职责 </p>
 *
 * @author ckmike
 * @version 1.0
 * @date 18-12-7 下午3:19
 * @copyright ckmike
 **/
public class Test {

    public static void main(String[] args) {
        // 订阅专栏
        SubscriptionChannel channel = new SubscriptionChannel();

        // 三个用户
        Observer user = new User("Mike");
        Observer user1 = new User("William");
        Observer user2 = new User("Crane");

        // 用户订阅专栏,开启一个线程进行订阅
        new Thread(()->{
            // 假设读者订阅付费的间隔是10s
            try {
                channel.register(user);
                TimeUnit.SECONDS.sleep(5);
                channel.register(user1);
                TimeUnit.SECONDS.sleep(5);
                channel.register(user2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();

        // 专栏作者线程进行发布文章,每隔一秒发布一篇文章
        new Thread(()->{
            int i = 1;
            while (true){
                channel.sendMessage("JAVA设计模式之观察者模式!-第"+i+"章");
                try {
                    TimeUnit.SECONDS.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                i++;
                if(i>5){
                    break;
                }
            }
        }).start();


        // 中途有用户退出订阅专栏,同时有人加入订阅
        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(7);
                channel.remove(user1);
                TimeUnit.SECONDS.sleep(5);
                channel.register(new User("Nill"));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();

    }

}

那么到这里我们就实现了订阅专栏消息推送。是不是很简单。那么我这里再给大家提供两个思考问题:1.如何让后来订阅的读者能够收到作者之前发布的消息呢?2.Spring的事件监听是如何实现的呢?如果你想到了欢迎在下面留言分析,和大家讨论哟。好!打完收工!