设计模式 | 观察者模式/发布-订阅模式(observer/publish-subscribe)

定义:

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

结构:(书中图,侵删)

一个抽象的观察者接口,拥有一个更新方法
若干个具体的观察者类
一个抽象的subject类,包含一个抽象观察者的集合,并拥有增加、删除观察者的方法,以及一个通知的方法
若干个具体subject类

实例:

举个看球赛的例子吧。
假设比赛在主场比。
subject就是球赛赛况;具体的subject分为现场看、电视上看。
然后观察者分为主场球迷,客场球迷。
 
抽象观察者接口(球迷接口):
package designpattern.observer;

public interface Fans {
    public void react(String result);
}
具体球迷类:
主场球迷:
package designpattern.observer;

public class HomeFans implements Fans {

    @Override
    public void react(String result) {
        System.out.print("主场球迷:");
        if (result.equals("win")) {
            System.out.println("Yes! 我们赢啦,主场就是牛批!!!");
        } else {
            System.out.println("输了,哎...");
        }
    }

}
客场球迷:
package designpattern.observer;

public class AwayFans implements Fans {

    @Override
    public void react(String result) {
        System.out.print("客场球迷:");
        if (result.equals("win")) {
            System.out.println("输了,哎...");
        } else {
            System.out.println("Yes! 客场都赢啦,牛批!!!");
        }
    }

}
抽象subject接口(比赛接口):
package designpattern.observer;

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

public interface Match {
    List<Fans> fansList = new ArrayList<>();// 这一句

    void addFans(Fans fans);

    void removeFans(Fans fans);

    void notifyResult(String result);

}
注释的那一句说明一下:
书中这句就是写在接口里的,但是写在这里就是一个全局的,所有子类都共用这同一个list
但我自己感觉各个子类所需要通知的观察者应该是不一定一样的,所以我是改到了子类中
但我后来写总结的时候回看定义,发现,总结写的就是“一对多的依赖关系”,既然不是多对多,那自然观察者就是统一调配的
只不过还是感觉有点怪怪的,往集合里添加删除观察者的方法是不是改成静态方法会好一点?
不然,必须要实例化一个子类的对象才能调用。
 
现场看比赛:
package designpattern.observer;

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

public class MatchOnTheSpot implements Match {
    // List<Fans> fansList = new ArrayList<>();

    @Override
    public void addFans(Fans fans) {
        fansList.add(fans);
    }

    @Override
    public void removeFans(Fans fans) {
        fansList.remove(fans);
    }

    @Override
    public void notifyResult(String result) {
        for (Fans fans : fansList) {
            fans.react(result);
        }
    }
}
电视看比赛:
package designpattern.observer;

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

public class MatchOnTheTV implements Match {
    // List<Fans> fansList = new ArrayList<>();

    @Override
    public void addFans(Fans fans) {
        fansList.add(fans);
    }

    @Override
    public void removeFans(Fans fans) {
        fansList.remove(fans);
    }

    @Override
    public void notifyResult(String result) {
        for (Fans fans : fansList) {
            fans.react(result);
        }
    }
}
此例中,两个子类的代码完全相同,可以把接口改成抽象类,然后把方法写到父类中,我这里就不改了。
 
客户端:
package designpattern.observer;

public class Client {
    public static void main(String[] args) {
        HomeFans homeFans = new HomeFans();
        AwayFans awayFans = new AwayFans();

        // 现场看
        Match match = new MatchOnTheSpot();
        match.addFans(homeFans);
        match.addFans(awayFans);
        System.out.println("主场赢啦~");
        match.notifyResult("win");

        System.out.println("===========================");

        // 电视上看
        match = new MatchOnTheTV();
        System.out.println("主场输啦~");
        match.notifyResult("lose");

    }
}
结果输出:
主场赢啦~
主场球迷:Yes! 我们赢啦,主场就是牛批!!!
客场球迷:输了,哎...
===========================
主场输啦~
主场球迷:输了,哎...
客场球迷:Yes! 客场都赢啦,牛批!!!

总结:

(按理说总结才是精华,但我每次总结都不知道要写点什么,可能是理解得还不够深刻吧。)
这个设计模式可以在一个对象改变的同时需要其他对象也跟着改变的时候使用。
好处说来说去就是那么几点,解除了观察者和通知者之间的耦合,将依赖转移给抽象,符合依赖倒转原则。
缺点嘛,就是这里的通知调用的都是同一方法(本例中就是notifyResult()方法),而实际应用中,可能是各种各样的方法名,还有可能有一些是别人已经封装好的方法,根本不可能去继承你的抽象观察者接口。
书中使用了一个叫事件委托的技术来解决这个问题,然后java中并没有直接的封装,实现这个需要用到反射,稍微有点麻烦,我这里就先不实现了,等以后再回来补全。有兴趣的可以自己去试一下。
 
posted @ 2019-04-25 22:09  莫愆  阅读(1028)  评论(0编辑  收藏  举报