设计模式——观察者模式

146 阅读2分钟

概述

在观察者模式中,定义对象之间的 一对多依赖关系,每当一个对象状态发生改变时,其相关依赖对象都会得到通知并自动更新。

该模式主要有如下角色:

  • 抽象目标Subject
  • 具体目标ConcreteSubject
  • 抽象观察者
  • 具体观察者

示例

以手机app推送信息为例,app中推送的信息分为比赛信息,新闻信息等,每当出现一个新信息,它会被推送给订阅用户。此处新信息是被观察者,订阅用户是观察者。

首先定义一个新闻类,它不属于模式的任意一个角色、

public class News {
    private String title;
    private String content;

    //省略get/set方法
}

抽象观察者:用户

public interface People {
    void receiveNews(News news);
}

抽象目标:可以理解为app的各种推送信息的大致定义,它持有一个管理所有订阅用户的数组。

public interface Subject {
    List<People> subPeople = new ArrayList<>();

    default void addSubscriptionPeople(People people) {
        subPeople.add(people);
    }

    default void remove(People people) {
        subPeople.remove(people);
    }

    void sendNews();
}

具体目标——负责将新闻信息推送给订阅用户类NewsSubject

public class NewsSubject implements Subject {
    @Override
    public void sendNews() {
        for(People people : subPeople) {
            News news = new News();
            news.setTitle("震惊!!!三亿中国人都看哭了,不转不是中国人");
            news.setContent("呱");
            people.receiveNews(news);
        }
    }
}

具体观察者——各种订阅用户类:路人A,路人B

public class PeopleA implements People {
    @Override
    public void receiveNews(News news) {
        System.out.println("?呱呱呱");
    }
}
public class PeopleB implements People {
    @Override
    public void receiveNews(News news) {
        System.out.println("?咕咕咕");
    }
}

测试一下:

public class Test {
    public static void main(String[] args) {
        Subject subject = new NewsSubject();
        subject.addSubscriptionPeople(new PeopleA());
        subject.addSubscriptionPeople(new PeopleB());
        subject.sendNews();
    }
}

总结

优点

(1)目标(推送信息)和观察者(订阅用户)之间是一个松耦合关系:目标只知道当前有哪些观察者(如本例中通过ArrayList管理订阅用户),并发送推送信息,但它不知道观察者后续会执行怎样的操作。

(2)符合开闭原则:若需要增加一个观察者(如增加一个路人C),只需写一个实现抽象观察者的类即可。

缺点

(1)在测试类中,需要知道所有的观察者,并执行增加/删除观察者操作。

(2)若一个被观察者对象有很多的直接和间接的观察者,则所有的观察者收到通知会花费很多时间。

(3)若被观察者之间有循环依赖,则可能会触发它们之间循环调用,导致系统崩溃。


参考资料

三种方式实现观察者模式