今天本想一本正经的把RxJava看一看,想着前段时间RxJava都已经到了第二版,而自己RxJava的认识还只是很基础,甚至连基础都算不上,所以本着以后能在项目里优雅地把他用出来的想法,我开始了RxJava学习之旅。可谁知天不遂人愿,在研究RxJava时候,遇到了两个守门人,那就是基本语法中的观察者(observe)和被观察者(observable)。看了两三遍大神们对这两个大爷的理解,还是有点云里雾里的感觉。他们都说,高手的世界像星空,你看得见,但你却看不懂。不幸中枪,但高手还是留下了一些蛛丝马迹给我,让我得以拔云见雾,得其真经。想要真正搞懂RxJava中的这两个概念,从Java观察者模式来进行理解我想效果可能会好些。本文我还是会以故事的形式,来将这些概念串连起来。
Now let's begain!娱乐新闻对于很多人来说都是生活中不可或缺的调味剂,而这些娱乐新闻的来源也是由娱乐记者(Reporter)时时刻刻坚守在岗位上而换来的。前不久歌坛有冉冉升起了一颗新星,他就是桑伦(Singer),桑伦在出道时,就已经被其经纪公司培养成了遵纪守法,从不胡来的优秀艺人,所以,他的迷妹也是数不甚数的。有了良好的群众基础,兢兢业业的娱乐记者也就按捺不住记录桑伦私生活的心了。那么,麻烦就来了每天早晨醒来桑伦家的花园里总是会有那么几个敬业的记者蹲守,想要得知他的动向。这样的行为要是换做别人也许早就报警了,不过多亏桑伦是个训练有素的优秀艺人,所以,他会把一些能告诉记者们的消息在第一时间传递给他们,而不是驱赶他们。这样记者能完成自己的工作,他也不会总是被纠缠。但为了安全起见,每个进出花园(Garden)的记者都需要进行一次登记。这这样桑伦很清楚自己的消息通知了哪些记者。别说桑伦那么大的明星都需要进行培训,那么记者也需要进行培训,按照公司要求的流程(Course)来进行新闻编写和发布。记者们得到桑伦的消息之后,则会在第一时间进行处理和发布新闻。这样看来记者和明星如果都给彼此应有的尊重,那还是挺和谐的嘛,我们这些吃瓜群众可能也会看的更开心:P。
那么这个小故事讲完了,我再来捋一捋其中故事中提示词究竟是对应了Java观察者模式中的哪些角色。
桑伦(Singer)——具体主体角色。他是真实存在的实体,而且也是被记者所观察的对象。
花园(Garden)—— 抽象主体角色。他是帮助桑伦(Singer)对观察他的人进行记录的,也就是负责注册,移除观察者的。而且当桑伦有新动向时,花园里的记者也会在第一时间知晓。
记者(Reporter)——具体观察者角色。他是观察桑伦的人,也就是说桑伦在有什么动向时,他们会在第一时间得到情报。
流程(Course)——抽象观察者角色。记者因为有了这一培训所以才能正常的将新闻发布出去。所以这是一个抽象的概念,也就是一个接口,实现它其中方法的必定是记者这个实际存在的对象。
说了那么多,我们来看看整个故事所组成的代码。
首先是抽象主体角色 花园(Garden)
public abstract class Garden {
//用一个list来存放登记进入花园的观察者(记者)
private ArrayList<Reporter> observerList = new ArrayList<>();
//进入时要添加
public void registReporter(Reporter reporter){
observerList.add(reporter);
}
//离开时要移除
public void removeReporter(Reporter reporter){
observerList.remove(reporter);
}
//在花园中的人有资格在第一时间得到最新消息,并更新新闻
protected void notifyEveryone(String news){
for (Reporter reporter : observerList){
reporter.submitNews(news);
}
}
}
具体主体角色 桑伦(Singer)
public class Singer extends Garden{
private String news = "我是桑伦";
public Singer() {
}
public void haveNews(String news){
this.news = news;
System.out.println("(桑伦)我现在的动作是:"+news);
//只要自己有了新动作,花园里的记者就会在第一时间知道
this.notifyEveryone(news);
}
}
抽象观察者角色 课程(Courese)
public interface Course {
void submitNews(String news);
}
具体观察者角色 记者(Reporter)
public class Reporter implements Course{
public Reporter() {
}
//根据培训后,按照指定的方法发布新闻
@Override
public void submitNews(String news) {
System.out.println("特大新闻! 桑伦"+news);
}
}
吃瓜群众 结果的享用者
public class Audience {
public Audience() {
}
public static void main(String[] args) {
Singer mr_sanglun = new Singer();
//两个记者进入花园(也就是两个观察者订阅主体)
Reporter reporter1 = new Reporter();
Reporter reporter2 = new Reporter();
mr_sanglun.registReporter(reporter1);
mr_sanglun.registReporter(reporter2);
mr_sanglun.haveNews("一口气吃了二十个苹果");
mr_sanglun.haveNews("在家里连续做了二十个后手翻");
}
}
运行结果
(桑伦)我现在的动作是:一口气吃了二十个苹果
特大新闻! 桑伦一口气吃了二十个苹果
特大新闻! 桑伦一口气吃了二十个苹果
(桑伦)我现在的动作是:在家里连续做了二十个后手翻
特大新闻! 桑伦在家里连续做了二十个后手翻
特大新闻! 桑伦在家里连续做了二十个后手翻
这就是观察者模式的第一种模型,也就是推模型,将主体的部分或者所有消息一并交给观察者,无论他们是否真的需要。这样做会让我们感觉不太优雅,如果把整个对象给观察者,让他们自己按需索取那不就很完美了?所以,观察者模式的第二种模型,拉模型
就会可以让我们实现这一设想,使得代码变得优雅,同时也满足了观察者的需要。
让我接着把故事讲下去,桑伦在娱乐圈风生水起,不仅唱歌,而且也开始涉足表演,这不自己主演的新电影就快要上映了。但是,自己花园里的记者所需要采集自己动向的方面却各不相同,有的只是报道自己的日常生活,而有的却关心自己的演艺事业。这样一来,自己不仅要把日常动向和所有花园里的记者说一遍,还要把自己快要上映的电影和他们再说一次,这样才能达到两方面的宣传效果,这样一来自己的工作增加了而且也变得十分劳累。所以,他的经济人给他整理了一下材料,让他定期开一次新闻发布会(Meeting),让花园里的记者一起来参加,这样不同的记者也能在新闻发布会上通过提问得到想要的新闻题材。那么花园对于记者进出记录的职责也就交给了新闻发布会(Meeting)。也是通过新闻发布会让每个记者得到了桑伦这个对象,从而向他索取需要的信息。桑伦很开心的接收了这个活动,所以每次都如约而至,向大家发布不同的信息。
故事讲完了,桑伦的行事风格发生了变化,让大家能够各取所需,同时也方便了自己。这个故事想表达的就是拉模型的优势。他是将整个主体角色对象直接交给了观察者,有观察者从主体那里获得自己所需要的信息。这样以来就能很精确的得到信息并且进行相应的处理。
现在来看看修改过后的代码
首先是抽象主体角色 新闻发布会(Meeting)
public abstract class Meeting {
private ArrayList<Reporter> observerList = new ArrayList<>();
public void registReporter(Reporter reporter){
observerList.add(reporter);
}
public void removeReporter(Reporter reporter){
observerList.remove(reporter);
}
protected void notifyEveryone(){
for (Reporter reporter : observerList){
reporter.joinMeeting(this);
}
}
}
具体主体角色 桑伦(Singer)
public class Singer extends Meeting{
private String normalNews = "日常新闻";
private String movieNews = "电影新闻";
public Singer() {
}
public void haveNormalNews(String normalNews){
this.normalNews = normalNews;
System.out.println("(桑伦)我现在的动作是:"+normalNews);
this.notifyEveryone();
}
public void haveMovieNews(String movieNews){
this.movieNews = movieNews;
System.out.println("(桑伦)我现在的动作是:"+movieNews);
this.notifyEveryone();
}
public String getMovieNews() {
return movieNews;
}
public String getNormalNews() {
return normalNews;
}
}
抽象观察者角色 课程(Courese)
public interface Course {
void joinMeeting(Meeting meeting);
}
具体观察者角色 记者(Reporter)
public class Reporter implements Course{
private Singer singer;
public Reporter() {
}
@Override
public void joinMeeting(Meeting meeting) {
this.singer = (Singer)meeting;
}
public void havaNormalNews(){
System.out.println("特大日常新闻! 桑伦 "+singer.getNormalNews());
}
public void havaMovieNews(){
System.out.println("特大电影新闻! 桑伦 "+singer.getMovieNews());
}
}
吃瓜群众 结果的享用者
public class Audience {
public Audience() {
}
public static void main(String[] args) {
Singer mr_sanglun = new Singer();
Reporter normalReport = new Reporter();
Reporter movieReport = new Reporter();
mr_sanglun.registReporter(normalReport);
mr_sanglun.registReporter(movieReport);
//发布了关于日常生活的信息
mr_sanglun.haveNormalNews("一口气吃了二十个苹果");
//日常生活的记者将其整理发布
normalReport.havaNormalNews();
//发布了关于电影的信息
mr_sanglun.haveMovieNews("在家里连续做了二十个后手翻");
//电影记者将其整理发布
movieReport.havaMovieNews();
}
}
运行结果
桑伦)我现在的动作是:一口气吃了二十个苹果
特大日常新闻! 桑伦 一口气吃了二十个苹果
(桑伦)我现在的动作是:在家里连续做了二十个后手翻
特大电影新闻! 桑伦 在家里连续做了二十个后手翻
这个是第二个模式拉模式的全部代码,我认为将Java观察者模式中的各个不同角色理解通过这个故事理解后,对于这种编程防方式的理解是很有帮助的。如果有哪些地方出现了笔误或者理解不到位、出错的地方,请和我联系。如果这个故事不仅帮我记住了这个模式,同时也帮到了你,不胜荣幸!