设计模式:观察者模式(基于 Java)
先给定义:定义了对象之间一对多的依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
这是《Head First 设计模式》一书中给出的定义,看起来有点玄乎,云里雾里的,但是简单说来,就是一个 listener(监听),它的用处在于:
主要用于控件的事件的监听和异步处理时的回调。
下面是平时编程中常用的示例。
1. 按钮点击事件的监听
观察者模式用的最多的情况,就是给控件设置各种事件监听了,这里以 Button 控件为例,通过 setOnClickListener() 方法添加观察者对象,当点击按钮时,按钮内部就会回调我们传入的观察者对象中的方法,这里调用的是 onClick() 方法,如下所示:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//获取按钮对象
Button btnStart = (Button) findViewById(R.id.btn_start);
//传入观察者对象,监听按钮点击事件
btnStart.setOnClickListener(new ClickListener());
}
/*
* 声明一个观察者类
*/
class ClickListener implements View.OnClickListener{
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, "按钮事件监听", Toast.LENGTH_SHORT).show();
}
}
测试结果:
这样,一个简单的观察者模式就实现了,当然,里面大部分用的是系统的实现代码,我们只是使用者。
下面,我们自己写一个网络异步请求观察者模式。
2. 网络异步请求的监听
现如今的编程,网络通讯已经是必不可少的一部分了,当网络请求在线程中运行时,我们必须在主线程中知道当前网络请求的状态与结果,以便给予用户反馈,这个时候,就要用到我们的观察者了。
- 创建观察者:网络请求监听类
这是自定义的观察者,这里为了偷懒,是以类的形式实现的,这种方法不是很好,一般是采用接口的方式实现。
/**
* 网络请求监听类
*/
public class HttpRequestListener {
//日志输出标签
public static final String TAG = "HttpRequestListener";
//请求成功监听
//参数类型与个数可以根据需要自己定义
public void requestCompleted(String response){
Log.d(TAG,response);
}
//请求失败监听
//参数类型与个数可以根据需要自己定义
public void requestFailed(Exception e){
Log.d(TAG,e.getMessage());
}
}
- 创建被观察者:网络请求类
网络请求类,作为被观察者,采用GET方式,分为异步与同步,但是同步请求方法不能在主线程中直接调用。
/**
* 网络请求类
*/
public class HttpUtils {
//异步监听,使用GET方式
public void requestGetAsyn(final String url, final HttpRequestListener listener){
//启动线程
new Thread(
new Runnable() {
@Override
public void run() {
String response = "";
try {
//开始网络请求
response = requestGet(url);
if(listener!=null){
//回调观察者对象的方法,请求成功
listener.requestCompleted(response);
}
} catch (IOException e) {
if(listener!=null){
//回调观察者对象的方法,请求失败
listener.requestFailed(e);
}
}
}
}
).start();
}
//网络请求,GET方式
public String requestGet(String url) throws IOException {
StringBuilder response = new StringBuilder();
InputStream inputStream;
BufferedReader reader = null;
try {
HttpURLConnection urlConnection = (HttpURLConnection) new URL(url).openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.setReadTimeout(5000);
urlConnection.setConnectTimeout(5000);
if(urlConnection.getResponseCode()==200){
inputStream = urlConnection.getInputStream();
reader = new BufferedReader(new InputStreamReader(inputStream));
String line;
while ((line = reader.readLine())!=null){
response.append(line);
}
}
} catch (IOException e) {
throw e;
}
finally {
if (reader != null) {
//关闭网络流
reader.close();
}
}
return response.toString();
}
}
- 关联观察者与被观察者
创建完观察者与被观察者,接下来就是两者的关联,说专业点就是两者之间建立依赖关系;建立方式其实已经在被观察者中确定了,就是在 requestGetAsyn() 方法中传入观察者对象,当然,这只是其中一种方法,我还想到(看到)一种方法,就是在被观察者中专门声明方法和变量用于建立与解除依赖关系,在本例中,就是在 HttpUtils 类中声明,如下所示:
//观察者列表
protected List<HttpRequestListener> listeners = new ArrayList<>();
//建立依赖关系,添加观察者
public void addListener(HttpRequestListener listener){
this.listeners.add(listener);
}
//解除依赖关系,删除观察者
public void removeListener(HttpRequestListener listener){
this.listeners.remove(listener);
}
如此调用:
httpUtils.addListener(new HttpRequestListener());
如此回调即可:
for (HttpRequestListener requestListener : listeners){
requestListener.requestCompleted(response);
}
- 测试
HttpUtils httpUtils = new HttpUtils();
//添加观察者,可以多次调用,添加多个观察者
httpUtils.addListener(new HttpRequestListener());
//调用异步请求,并传入观察者对象
httpUtils.requestGetAsyn("http://blog.csdn.net/xwdoor",new HttpRequestListener());
对了,不要忘了添加网络访问权限,不然报错
<uses-permission android:name="android.permission.INTERNET"/>
3. 书中的代码示例
《Head First 设计模式》这本书中也有几个观察者的例子,如果有兴趣,可以去看看,这里就不再敲出来了,主要是太懒了
4. 总结
作为程序员,写出这篇文章,用了好几个小时,用到的所有代码都是现场用 Android Studio 敲出来,运行了一遍的,所以导致效率有点低,写代码、调试、写文章、调格式,很繁琐的事儿,还好坚持了下来,写完这篇文章,又把观察者模式温习了一遍,感觉挺好的,逐渐感觉到了分享、整理的成就,哈哈。
第一次使用 MarkDown 写文章,写这篇文章也算熟悉了基本语法,有点小激动,小兴奋。
随着写的文章的增多,相信速度会有所提高;最近学习 Android,看视频,敲代码,熟悉之后才会整理分享。