设计模式-观察者(一)
2016-05-27 17:43
225 查看
设计模式-观察者(一)
大家好,我们又见面了,还记得上次所写的 <<设计模式-策略模式>>么?,我们今天来学习另一个模式之前,先复习一下上次的”策略模式”是怎么定义的吧:
定义:
策略模式定义
:算法族,分别封装起来,让他们之间可以互相替换,次模式让算法的变化独立于使用算法的客户.
“策略模式”中我们还学到了两个设计原则:
1 :找出应用中可能需要变化之处,把他们独立起来,不要和那些不需要变化的部分的代码混合在一起.2 :针对借口编程,而不是针对实现编程.
思考……………
经过对”策略模式”的思考,是不是有点印象了? 下面我们继续向下一个模式进发把……..这次我们先推出这个模式,名字为”观察者模式”, 它的定义
: 观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新.
不懂?
当然,如果能现在就能明白其真谛的话,你可以关浏览器了,以后都不需要再来GC博客了……(开玩笑 ^_^)我们一步一步来,先了解一下什么是观察者模式.
*不知道你订没订阅报纸(如果你从小到大没订阅过报纸或者书刊杂志,我只能说你错过了一个生活体验..) 好吧,瞎扯了,我们先介绍一下订阅报刊的过程:
假如你家附近有一所报社,而你又是<<意林>>(高中生应该都读过的)的爱好者,你当然会迫不及待的去订阅一年属于自己的<<意林>>.当你去报社订阅了<<意林>>之后,每当报社收到新出版的<<意林>>,它就会将你的那份送到你的家中供你阅读.*
好吧.整个流程应该弄明白了吧,是在不明白可以亲自去订阅一年的<<意林>>体验一把哈.
到这里,可能你会想:订阅报刊与”观察者模式”有什么关系啊~
我只能回答你:其实订阅报刊整个流程就是"观察者模式"的整个流程,也就是说
"订阅报刊"用了"观察者模式".
当然,如果你已经想到了,那么只能说你的智商真的是太高了(@_@)
报社就相当于”观察者模式”里的”发布者(publish)/主题(Subject)”,而你就相当于”观察者模式”里的”订阅者(Subscribe)/观察者(Observer)”
“观察者模式”介绍完毕~~~
骗你的,代码还没写,怎么可能介绍完毕呢.况且”观察者模式”有好多种,今天只介绍其一….现在我们开始写代码吧…用代码来实现上面的”订阅报刊”的功能…
//定义报社类 public class NewspaperOffice { //定义主题后期会更新的数据 private int updatedata; //通知所有的<<意林>>订阅者 @Override public void notifyObservers() { //订阅者1订阅了意林 Subscriber1 s1 = new Subscriber1(); //让订阅者1更新数据,也就是给订阅者1送去<<意林>> s1.update(this.updatedata); //订阅者2订阅了意林 Subscriber1 s2 = new Subscriber2(); //让订阅者2更新数据,也就是给订阅者2送去<<意林>> s2.update(this.updatedata); } //当主题里的数据进行更新的时候,通知所有的观察者 public void setUpdatedata(int updatedata) { this.updatedata = updatedata; notifyObservers(); } } //定义订阅者1类 public class Subscriber1 { //定义订阅者自己的信息 private String name; //定义订阅者需要在报社那里接到的数据 private int updatedata; //构造器里还装入了该订阅者的名字 public Subscriber1(String name){ this.name = name; } public void display() { System.out.println("我是订阅者,我的名字叫: "+name+" , 这是从报社接到的数据: "+ updatedata); } public void update(int updatedata) { this.updatedata = updatedata; display(); } } //定义订阅者2类 public class Subscriber2 { //定义订阅者自己的信息 private String name; private int age; //定义订阅者需要在报社那里接到的数据 private int updatedata; //构造器里还装入了该订阅者的名字 public Subscriber1(String name, int age){ this.name = name; } //显示出信息 public void display() { System.out.println("我是订阅者,我的名字叫: "+name+", 我的年龄是: "+age+" , 这是从报社接到的数据: "+ updatedata); } //更新订阅者手中的信息(也就是更新手中的<<意林>>书的个数) public void update(int updatedata) { this.updatedata = updatedata; display(); } }
看了上面的代码,你有没有一种想骂我的感觉,上面的代码太不灵活了,当然这不可能是”观察者模式”哈,我只是写了一个
java初学者会设计的模式.
上面代码很不灵活,的确.如果有一天,又有一个”订阅者3”去报刊订阅<<意林>>,
NewspaperOffice这个类就需要重新写代码,在
notifyObservers()方法里添加”订阅者3”的实例…当有一天”订阅者2”不想订阅了,
NewspaperOffice类又要重新重构代码.麻烦至极啊…
很显然啊,上面的代码太别没有遵循之间我们所说的两个设计原则,所有是一组极差,及其不具备灵活性的代码…
下面,我们用”观察者模式”编写上面的代码…
首先,我们需要面向接口编程,把
NewspaperOffice与
Subscriber1,
Subscriber2都提取一下.
/* 这里提供了一个Subject(主题)接口,接口里定义了:注册观察者方法,删除观察者方法,向已经注册的观察者发送消息的方法 */ public interface Subject { //用来注册观察者 public void registerObserver(Observer o); //用来删除观察者 public void removeObserver(Observer o); //通知所有已经注册的观察者 public void notifyObservers(); } /* 定义Observer(观察者)接口,所有观察者都具备更新内部数据的功能 */ public interface Observer { //更新观察者内部的数据 public void update(int updatedata); } /* 定义Subscriber(订阅者)接口,所有订阅者都可以展出自己的内部信息(也就是所有订阅者都可以向别人展出手中意林数目 */ public interface Subscriber { //每一个订阅者都可以显示自己的信息 public void display(); } /* 下面,我们的报社出厂了 */ import java.util.ArrayList; import java.util.List; //报社对象实线了主题借口 public class NewspaperOffice implements Subject { //定义主题后期会更新的数据 private int updatedata; //建立一个list来装注册进来的观察者 private List <Observer>observers; //构造函数,构造list public NewspaperOffice(){ observers = new ArrayList<Observer>(); } //添加注册观察者 @Override public void registerObserver(Observer o) { observers.add(o); } //移除观察者 @Override public void removeObserver(Observer o) { int i = observers.indexOf(o); if(i >= 0){ observers.remove(i); } } //通知所有的观察者 @Override public void notifyObservers() { for(Observer observer: observers){ observer.update(this.updatedata); } } //当主题里的数据进行更新的时候,通知所有的观察者 public void setUpdatedata(int updatedata) { this.updatedata = updatedata; notifyObservers(); } } /* 定义订阅者1 实现了观察者接口,订阅者的接口 */ public class Subscriber1 implements Observer, Subscriber { //定义订阅者自己的信息 private String name; //定义订阅者需要在报社那里接到的数据 private int updatedata; //定义观察者引用 private Subject subject; //构造器里接受到该观察者需要注册的到哪个主题。并且注册到该主题上 //构造器里还装入了该订阅者的名字 public Subscriber1(Subject subject, String name){ this.name = name; this.subject = subject; subject.registerObserver(this); } @Override public void display() { System.out.println("我是订阅者,我的名字叫: "+name+" , 这是从报社接到的数据: "+ updatedata); } @Override public void update(int updatedata) { this.updatedata = updatedata; display(); } } /* 定义订阅者2,基本内容与订阅者1相似,但是请注意,订阅者1与订阅者2是完全不同的两个类型,因为订阅者2里需要填写年龄信息. 如果两个类一模一样,那写成一个类不就好了么? 为什么要这样设计两个类呢? 因为我们后期的订阅者是各种各样的,订阅者怎么变化都无所谓,这充分说明了我们类与类之间的耦合程度非常低. */ public class Subscriber2 implements Observer, Subscriber { //定义订阅者自己的信息 private String name; private int age; //定义订阅者需要在报社那里接到的数据 private int updatedata; //定义观察者引用 private Subject subject; //构造器里接受到该观察者需要注册的到哪个主题。并且注册到该主题上 //构造器里还装入了该订阅者的名字 public Subscriber2(Subject subject, String name, int age){ this.age = age; this.name = name; this.subject = subject; subject.registerObserver(this); } @Override public void display() { System.out.println("我是订阅者,我的名字叫: "+name+", 我的年龄是: "+age+" , 这是从报社接到的数据: "+ updatedata); } @Override public void update(int updatedata) { this.updatedata = updatedata; display(); } }
基本结构都已经设计完了,现在我来写个测试类进行测试
public class Main { public static void main(String[] args) { //创建报社 NewspaperOffice nsp1 = new NewspaperOffice(); //用Subscriber1创建订阅者1,并且将他注册到报社 Subscriber1 GC = new Subscriber1(nsp1, "GC"); //用Subscriber2创建订阅者2,也注册到报社 Subscriber2 YP = new Subscriber2(nsp1, "YP", 33); System.out.println("===========下面开始发布第1版报纸,此时GC与YP都在报社里========"); //现在报社开始发布第一版报纸 nsp1.setUpdatedata(1); System.out.println("===========下面开始发布第2版报纸,此时GC与YP都在报社里========"); //报社发布第二版报纸 nsp1.setUpdatedata(2); //现在GC不订阅报纸了,报社删除GC nsp1.removeObserver(GC); System.out.println("===========下面开始发布第3版报纸,此时只有YP都在报社里========"); //删除GC之后,报社又发布了第三版报纸 nsp1.setUpdatedata(3); /* * 运行结果如下: * ===========下面开始发布第1版报纸,此时GC与YP都在报社里======== * 我是订阅者,我的名字叫: GC , 这是从报社接到的数据: 1 * 我是订阅者,我的名字叫: YP, 我的年龄是: 33 , 这是从报社接到的数据: 1 * ===========下面开始发布第2版报纸,此时GC与YP都在报社里======== * 我是订阅者,我的名字叫: GC , 这是从报社接到的数据: 2 * 我是订阅者,我的名字叫: YP, 我的年龄是: 33 , 这是从报社接到的数据: 2 * ===========下面开始发布第3版报纸,此时只有YP都在报社里======== * 我是订阅者,我的名字叫: YP, 我的年龄是: 33 , 这是从报社接到的数据: 3 */ } }
这是“观察者”模式的类图,里面只有Subject类与Observer类之间的关系,请仔细思考。
好吧,我承认,”观察者模式”不是那么容易理解,如果你看了我上面所写的内容之后就的能理解”观察者模式”,我真的再次赞叹一下:你的智商真的很高很高….
经过对将近两个小时的奋斗,终于把<<设计模式-观察者模式>>写完了,如果有不会的,没看懂的请
留言给我……………
请转发OR复制的同学,标注出处
,尊重作者劳动成果
,谢谢亲
谢谢观看,结束本节!注:本文借鉴了《HeadFirst-设计模式》一书,作者总结的内容不到书内容精彩十分之一,还望大家多读原著。
相关文章推荐
- WebService笔记(二):简介
- session和cookie是什么,登录,权限控制,不登录不让访问资源
- oracle 11g里边有scott用户,且脚本scott.sql无错 SQL> conn scott 输入口令: ERROR: ORA-01017: invalid username/passwor
- Qt之布局管理器
- iOS AFNetworking 3.0
- hdoj-1563-Find your present!
- background-position的百分比是怎么计算定位的
- 安装pods 遇到的坑
- iOS解决警告ld: warning: directory not found for option ''
- Qt之布局管理器
- Android 探究 LayoutInflater setFactory
- 快速排序的优化
- 自定义UI的时候,通常到CGContextRef进行绘图,以下为详细API的叙述
- 设计模式-策略模式
- iOS获取当前 年月日和星期
- ehcache memcache redis 三大缓存比较
- AppConext 的onCreate方法执行时机
- 操作系统
- 2016/5/27 1000. Hex to decimal
- Spark定制班第15课:Spark Streaming源码解读之No Receivers彻底思考