观察者模式
以NBA球星和媒体之间的关系为例,球星的一举一动都在媒体的关注之下,比如,球星去夜店做了什么什么事情,媒体都会第一时间给出报导。
那么这里球星就属于被观察者,媒体属于观察者。
需要定义一个球员接口,包含球员可能出现的各种情况:
public interface BasketballPlayer {
/**
* 球员去夜店
*/
public void goNightclub();
/**
* 球员发推特
*/
public void publishTwitter();
}
其次需要定义一个新闻媒体的接口,每当球员有新的情况,就会更新消息:
public interface NewsMedia {
public void update(String message);
}
我们以哈登去夜店,发推特为例,其中还需要为哈登单独定义两个flag变量,是不是去夜店啦,是不是发推特啦,以及他们对应的get,set方法。
public class Harden implements BasketballPlayer{
private boolean isGoNightclub = false;
private boolean isPublishTwitter = false;
/**
* 哈登去夜店了
*/
public void goNightclub() {
setGoNightclub(true);
}
/**
* 哈登发推特了
*/
public void publishTwitter() {
setPublishTwitter(true);
}
public boolean isGoNightclub(){
return isGoNightclub;
}
public void setGoNightclub(boolean isGoNightclub){
this.isGoNightclub = isGoNightclub;
}
public boolean isPublishTwitter(){
return isPublishTwitter;
}
public void setPublishTwitter(boolean isPublishTwitter){
this.isPublishTwitter = isPublishTwitter;
}
}
新闻媒体以ESPN为例:
public class ESPN implements NewsMedia{
public void update(String type) {
System.out.println(type);
}
}
ESPN肯定不会一天24小时都花在哈登身上,于是,ESPN雇了一名记者Jack,全程跟踪报道哈登,一有消息就通知ESPN。
public interface Journalist extends Runnable{
}
/**
* Created by lovekun on 2016/10/21.
*
*/
public class Jack implements Journalist, Runnable{
private BasketballPlayer mBasketballPlayer;
private NewsMedia mNewsMedia;
private String mType;
/**
*
* @param basketballPlayer
* @param newsMedia
* @param type 监控内容
*/
public Jack(BasketballPlayer basketballPlayer, NewsMedia newsMedia, String type){
this.mBasketballPlayer = basketballPlayer;
this.mNewsMedia = newsMedia;
this.mType = type;
}
public void run() {
while (true){
synchronized (mBasketballPlayer){
if (mType.equals("Nightclub")){
if (((Harden)mBasketballPlayer).isGoNightclub()){
mNewsMedia.update("哈登去夜店了");
((Harden) mBasketballPlayer).setGoNightclub(false);
}
}else if(mType.equals("publishTwitter")){
if (((Harden)mBasketballPlayer).isPublishTwitter()){
mNewsMedia.update("哈登发推特了");
((Harden) mBasketballPlayer).setPublishTwitter(false);
}
}
}
}
}
}
这里需要注意更新哈登信息和状态,需要放在同步代码块中,以防哈登离开一家夜店后,Jack还没有来及更新它的状态,哈登又进了另一家夜店,导致Jack无法及时更新信息和通知ESPN。
剩下的就是场景类了:
/**
* Created by lovekun on 2016/10/21.
*
* 观察者模式场景类
*/
public class ObserverClient {
public static void main(String[] args) throws Exception{
BasketballPlayer mBasketballPlayer = new Harden();
NewsMedia mESPN = new ESPN();
Journalist journalistForNightclub = new Jack(mBasketballPlayer, mESPN, "Nightclub");
Thread threadForNightclub = new Thread(journalistForNightclub);
threadForNightclub.start();
Journalist journalistForPublishTwitter = new Jack(mBasketballPlayer, mESPN, "publishTwitter");
Thread threadForPublishTwitter = new Thread(journalistForPublishTwitter);
threadForPublishTwitter.start();
Thread.sleep(2000);
mBasketballPlayer.goNightclub();
Thread.sleep(2000);
mBasketballPlayer.goNightclub();
Thread.sleep(2000);
mBasketballPlayer.publishTwitter();
Thread.sleep(2000);
mBasketballPlayer.goNightclub();
}
}
这种方法通过一个线程里的死循环不断监听,如果监听的东西多了,会导致系统的性能极差。
既然球员一有动作,新闻媒体就会知道,可以把新闻媒体聚合到球员类中。
仍然定义一个球员接口和新闻媒体接口
/**
* Created by lovekun on 2016/10/21.
*/
public interface BasketballPlayer {
/**
* 球员去夜店
*/
public void goNightclub();
/**
* 球员发推特
*/
public void publishTwitter();
}
/**
* Created by lovekun on 2016/10/21.
*/
public interface NewsMedia {
public void update(String message);
}
实现一个被观察者的接口,包含对观察者的添加,删除,通知方法。
/**
* Created by lovekun on 2016/10/21.
*/
public interface Observable {
public void addObserver(NewsMedia newsMedia);
public void delObserver(NewsMedia newsMedia);
public void notifyObservers(String message);
}
实例如一个巨星哈登,他实现了球员接口,同时也是一个被观察者,实现了被观察者接口:
/**
* Created by lovekun on 2016/10/21.
*/
public class Harden implements BasketballPlayer, Observable{
private ArrayList<NewsMedia> newsMedias = new ArrayList<NewsMedia>();
/**
* 哈登去夜店了
*/
public void goNightclub() {
notifyObservers("哈登去夜店了");
}
/**
* 哈登发推特了
*/
public void publishTwitter() {
notifyObservers("哈登发推特了");
}
public void addObserver(NewsMedia newsMedia) {
this.newsMedias.add(newsMedia);
}
public void delObserver(NewsMedia newsMedia) {
this.newsMedias.remove(newsMedia);
}
public void notifyObservers(String message) {
for (NewsMedia newsMedia : newsMedias){
newsMedia.update(message);
}
}
}
关注哈登的媒体有很多,ESPN、腾讯等等
/**
* Created by lovekun on 2016/10/21.
*/
public class ESPN implements NewsMedia {
public void update(String message) {
System.out.println("ESPN新闻:" + message);
}
}
/**
* Created by lovekun on 2016/10/21.
*/
public class Tengxun implements NewsMedia{
public void update(String message) {
System.out.println("腾讯新闻:" + message);
}
}
剩下的就是场景类的实现:
/**
* Created by lovekun on 2016/10/21.
*
* 观察者模式场景类
*/
public class ObserverClient {
public static void main(String[] args) throws Exception{
BasketballPlayer harden = new Harden();
((Harden) harden).addObserver(new ESPN());
((Harden) harden).addObserver(new Tengxun());
harden.goNightclub();
harden.publishTwitter();
}
}
这样,如果还有虎扑,新浪等媒体也关注哈登,这些新闻媒体只需要实现NewMedia接口,并且harden的实例添加这些观察者即可。程序具有了更好的可扩展性。
- 参考内容: 《涉及模式之禅》
- 源码下载: 观察者模式源码