前言
在上一个博客中我们介绍了Strategy模式,它是行为型模式麾下的一员大将。那么本博客我们来学习一下行为型模式麾下的另一员大将Observer模式。
思考题
老套路,先来思考下面的问题:
问题:思考报纸订阅的问题,有一个体育报社每天都会有新的体育报道,关注体育新闻的用户会获取每天的报道
首先,我们还是来看一看不使用设计模式的代码:
SportNews.java:
public class SportNews { private String context; //用户获取新闻 public String getContext() { return context; } //产生新的新闻 public void newContext(String context) { this.context = context; } }
People.java:
public class People { private final String name; private SportNews sportNews; public People(String name, SportNews sportNews) { this.sportNews = sportNews; this.name = name; } //读新闻 public void readNews() { System.out.println(String.format("I am %s,I am looking %s", name, sportNews.getContext())); } }
Robot.java:
public class Robot { private static int num = 0; private final int id = num++; private SportNews sportNews; public Robot(SportNews sportNews) { this.sportNews = sportNews; } public void readNews() { System.out.println(String.format("Robot:%s,Context:%s", id, sportNews.getContext())); } }
测试程序:
TestMain.java:
public class TestMain { public static void main(String... ars) { SportNews sportNews = new SportNews(); People xiaoLi = new People("xiao li", sportNews); People xiaoMin = new People("xiao min", sportNews); Robot robot = new Robot(sportNews); sportNews.newContext("H:F 1:1"); xiaoLi.readNews(); xiaoMin.readNews(); robot.readNews(); sportNews.newContext("湖人:火箭 102:103"); xiaoLi.readNews(); xiaoMin.readNews(); robot.readNews(); } }
print:
I am xiao li,I am looking H:F 1:1 I am xiao min,I am looking H:F 1:1 Robot:0,Context:H:F 1:1 I am xiao li,I am looking 湖人:火箭 102:103 I am xiao min,I am looking 湖人:火箭 102:103 Robot:0,Context:湖人:火箭 102:103
上面的代码展示了xiaoli、xiaomin、Robot是怎样获取新的新闻内容的,每个人都需要调用 readNews 才可以获取新的新闻内容。如果再增加一个人,那么这个人也需要调用相应的方法才可以获取。这就相当于每个人都知道报社的地址,每天都要去报社自己拿新的报纸。那么怎样才能改变这样的关系,让人不需要去报社拿新的报纸,那就是使用观察这模式,下面就让我们来具体的介绍这个模式。
介绍Observser
- 定义: 定义对象之间一对多的关系,当一个对象的状态发生改变时,该对象所有依赖对象都会得到通知。
- 类图:
从上面的类图中可以总结一下几点:
- 抽象主题类(Observable):持有一个 Observer 的集合List,通过 registerObserver 方法可以将 Observer 注册到集合中。通过 notifyServers 方法可以让所有在集合里面的 Observer 都获得通知
- 抽象观察者类(Observer):持有一个 Observable ,其目的是为了在 Observable 的集合中取消订阅。而注册行为是发生在构造函数中
-
具体主题类(ConcreteObservable):继承了 Observable ,并且实现了父类的抽象方法 changeState ,在该方法中调用了父类的方法 notifyObservers
- 具体观察者类(ConcreteObserver):继承了 Observer ,并且实现了父类的方法 update 方法,该方法是具体观察者要做的动作
重构思考题
通过上面的学习,我们已经基本上了解了观察者模式了,下面我们针对上面提到的问题,使用观察者模式来实现它:
首先,我们定义一个抽象主题类:
Subject.java:
import java.util.List; import java.util.ArrayList; public abstract class Subject { protected List<Observer> observers; public Subject() { observers = new ArrayList<Observer>(); } public boolean registerObserver(Observer observer) { return observers.add(observer); } public boolean removeObserver(Observer observer) { return observers.remove(observer); } public void notifyObservers(String context) { for(Observer observer: observers) { observer.update(context); } } public abstract void newContext(String context); }
然后,我们来实现抽象观察者:
Observer.java:
public abstract class Observer { private Subject subject; public Observer(Subject subject) { this.subject = subject; subject.registerObserver(this); } public abstract void update(String context); }
之后,实现一个具体主题类:
SportNews.java:
public class SportNews extends Subject { @Override public void newContext(String context) { notifyObservers(String.format("sport news:%s", context)); } }
最后,实现具体观察者类:
People.java:
public class People extends Observer { private final String name; public People(String name, Subject subject) { super(subject); this.name = name; } @Override public void update(String context) { System.out.println(String.format("I am %s,I am looking %s", name, context)); } }
Robot.java:
public class Robot extends Observer { private static int num = 0; private final int id = num++; public Robot(Subject subject) { super(subject); } @Override public void update(String context) { System.out.println(String.format("Robot:%s,Context:%s", id, context)); } }
测试用例:
TestMain.java:
public class TestMain { public static void main(String... ars) { Subject subject = new SportNews(); People xiaoLi = new People("xiao li", subject); People xiaoMin = new People("xiao min", subject); Robot robot = new Robot(subject); subject.newContext("H:F 1:1"); subject.newContext("湖人:火箭 102:103"); } }
以上就是观察者模式的全部代码,通过使用观察者模式,可以看出我们只需要将观察者注册到主题中。不需要调用观察者的任何方法,就可以自动的获得通知。极大的减少了代码量和维护成本。这就相当于你只要订阅了报纸,每天都会有人给你送过去,而不需要自己去报社拿。
扩展
在我们的java中也定义了观察者模式的接口,并且Java中的事件监听机制就是使用观察者模式实现的,它们分别是 java.util.Observer 和 java.util.Observable 。通过实现这两个接口我们可以实现自己的观察者模式,下面就让我们来看看这两个类。
类图:
简单介绍下上面的类图:
- 抽象主题类(Observable): addObserver 是用来注册观察者的。 notifyObservers 是用来通知所有的观察者的。 setChanged 是用来告知 notifyObservers 状态已经改变,可以通知了。其他的就不做介绍了。
下面让我们使用java提供的观察者模式接口来实现上面的问题:
People.java:
import java.util.Observer; import java.util.Observable; public class People implements Observer { private final String name; private Observable observable; public People(String name, Observable o) { this.name = name; observable = o; observable.addObserver(this); } @Override public void update(Observable o, Object arg) { System.out.println(String.format("I am %s,I am looking %s", name, arg.toString())); } }
Robot.java:
import java.util.Observer; import java.util.Observable; public class Robot implements Observer { private static int num = 0; private final int id = num++; private Observable observable; public Robot(Observable observable) { this.observable = observable; observable.addObserver(this); } @Override public void update(Observable o, Object arg) { System.out.println(String.format("Robot:%s,Context:%s", id, arg.toString())); } }
SportNews.java:
import java.util.Observable; public class SportNews extends Observable { public void newContext(String context) { setChanged(); notifyObservers(context); } }
测试用例:
TestMain.java:
public class TestMain { public static void main(String... ars) { SportNews sportNews = new SportNews(); People xiaoLi = new People("xiao li", sportNews); People xiaoMin = new People("xiao min", sportNews); Robot robot = new Robot(sportNews); sportNews.newContext("H:F 1:1"); sportNews.newContext("湖人:火箭 102:103"); } }
以上是本博客的全部内容,希望看完会对你有一定的启发。