HardBirch

浅学设计模式之观察者<Observer>模式及在android中的应用

时间:12-05-18 栏目:安卓源码解析与小应用 作者:张飞不张,文采横飞 评论:4 点击: 2,831 次

          最近在学习下设计模式,而加深学习的不错的方法就是把心得写出来吧。记录下自己的理解。现在自己看的书是《head.Frist设计模式》这本书。比较不错,想看的朋友可以看下。

        观察者<Observer>模式(有时又被称为发布-订阅<Publish/Subscribe>模式、模型-视图<Model/View>模式、源-收听者<Source/Listener>模式或从属者<Dependents>模式)是软件设计模式的一种。在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实作事件处理系统。(源自百度百科)

           (五月三十一号修正内容)

        看下结构图:

     开始一个小例子

Observer接口:

     

public interface ISubscribe {
	void getNewPaper();
}

实现Observer接口的观察者:

个人订阅者:

public class PersonalSubscriber implements ISubscribe {
	private String strName;

	public void setNewsPaperName(String strName){
		this.strName = strName;
	}

	public String getNewsPaperName(){
		return strName;
	}

	@Override
	public void getNewPaper() {
		// TODO Auto-generated method stub
		System.out.println("我是个人用户,我得到了我的报纸:"+getNewsPaperName());
	}

}

企业订阅者:

public class EnterpriseSubscriber implements ISubscribe {
    private String strName;

	public void setNewsPaperName(String strName){
		this.strName = strName;
	}

	public String getNewsPaperName(){
		return strName;
	}
	@Override
	public void getNewPaper() {
		// TODO Auto-generated method stub
		System.out.println("我是企业用户,我得到了我的报纸:"+getNewsPaperName());
	}

}

被观察者:Subject

public abstract class Publish {
	public List<ISubscribe> list;
	public Publish(){
		list = new ArrayList();
	}

    public void registered(ISubscribe iSubscribe){
    	list.add(iSubscribe);
    }

    public void unregistered(ISubscribe iSubscribe){
    	list.remove(iSubscribe);
    }

    public abstract void sendNewsPaper();
}

这里使用的是抽象类

下面是实现:

public class PostOffice extends Publish{

	@Override
	public void sendNewsPaper() {
		// TODO Auto-generated method stub
		Iterator iterator = list.iterator();
		while(iterator.hasNext()){
			((ISubscribe) iterator.next()).getNewPaper();
		}
	}

}

测试:

public static void main(String[] args) {
		// TODO Auto-generated method stub
		PostOffice postOffice = new PostOffice();

		//得到个人用户
		PersonalSubscriber person = new PersonalSubscriber();
		person.setNewsPaperName("《南方周末》");
		//得到企业用户
		EnterpriseSubscriber enterprise = new EnterpriseSubscriber();
		enterprise.setNewsPaperName("《企业报》");

		//注册观察者
		postOffice.registered(person);
		postOffice.registered(enterprise);

		//发放报纸
		postOffice.sendNewsPaper();
	}

测试结果:

我是个人用户,我得到了我的报纸:《南方周末》
我是企业用户,我得到了我的报纸:《企业报》

下面是有些错误五月18号版本:后面有错误解析!

        举个例子,张三从邮局订阅了《南方周末》,李四从邮局订阅了《新京报》,王五从邮局里面订阅了《南方都市报》。当报纸抵达邮局的时候,邮局就会把报纸送递订阅者。而不需要订阅者天天到邮局询问报纸是否到达邮局。

       下面开始代码:首先应该是个订阅者接口:

package cn.demo;

public interface ISubscribe {
	//从邮局订阅报纸
	public void registered(PostOffice postOffice);
	//从邮局退订报纸
	public void unregistered(PostOffice postOffice);
	//获取报纸名称
	public void getNewsPaper();
}

第三个方法非必须,前两个方法必须要有。(应修改为前两个非必须,第三个方法必须有)

然后有三个订阅者类:

张三:

package cn.demo;

public class ZhangSan implements ISubscribe{
	private String mName;
	private String mNewsPaperName;

	public ZhangSan(String mName, String mNewsPaperName) {
		this.mName = mName;
		this.mNewsPaperName = mNewsPaperName;
	}

	public String getName() {
		return mName;
	}

	public String getNewsPaperName() {
		return mNewsPaperName;
	}

	@Override
	public void registered(PostOffice postOffice){
		postOffice.registeredNewsPaper(this);
	}

	@Override
	public void unregistered(PostOffice postOffice){
		postOffice.unregisteredNewsPaper(this);
	}

	@Override
	public void getNewsPaper() {
		// TODO Auto-generated method stub
		System.out.println("我是"+getName()+",我收到我订阅的"+getNewsPaperName());
	}

}

李四:

package cn.demo;

public class Lisi implements ISubscribe{
	private String mName;
	private String mNewsPaperName;

	public Lisi(String mName, String mNewsPaperName) {
		this.mName = mName;
		this.mNewsPaperName = mNewsPaperName;
	}

	public String getName() {
		return mName;
	}

	public String getNewsPaperName() {
		return mNewsPaperName;
	}

	@Override
	public void unregistered(PostOffice postOffice){
		postOffice.unregisteredNewsPaper(this);
	}

	@Override
	public void registered(PostOffice postOffice){
		postOffice.registeredNewsPaper(this);
	}

	@Override
	public void getNewsPaper() {
		// TODO Auto-generated method stub
		System.out.println("我是"+getName()+",我收到我订阅的"+getNewsPaperName());
	}

}

王五:

package cn.demo;

public class Wangwu implements ISubscribe{
	private String mName;
	private String mNewsPaperName;

	public Wangwu(String mName, String mNewsPaperName) {
		this.mName = mName;
		this.mNewsPaperName = mNewsPaperName;
	}

	public String getName() {
		return mName;
	}

	public String getNewsPaperName() {
		return mNewsPaperName;
	}

	@Override
	public void registered(PostOffice postOffice){
		postOffice.registeredNewsPaper(this);
	}

	@Override
	public void unregistered(PostOffice postOffice){
		postOffice.unregisteredNewsPaper(this);
	}

	@Override
	public void getNewsPaper() {
		// TODO Auto-generated method stub
		System.out.println("我是"+getName()+",我收到我订阅的"+getNewsPaperName());
	}

}

三个订阅者,都有方法订阅报纸,或者取消订阅。其实关于订阅者的信息,是存储在邮局类里面。

邮局类:(邮局类应该继承一个抽象类或者接口(Subject),这里没有实现)

import java.util.ArrayList;
import java.util.List;

public class PostOffice {
	private List<ISubscribe> SubscribeList = new ArrayList<ISubscribe>();

	public void registeredNewsPaper(ISubscribe subscribe) {
		SubscribeList.add(subscribe);
	}

	public void unregisteredNewsPaper(ISubscribe subscribe) {
		if (subscribe != null) {
			SubscribeList.remove(subscribe);
		}

	}

	public void getNewsPaper(boolean bool) {
		if (bool) {
			sendNewsPaper();
		}
	}

	public void sendNewsPaper() {
		for(ISubscribe subscribe : SubscribeList) {
			subscribe.getNewsPaper();
		}
	}
}

在这个类里面,有变化通知是使用的sendNewsPaper()这个方法,遍历所有的订阅者。
测试类:

public static void main(String[] args) {
		// TODO Auto-generated method stub
		PostOffice mPostOffice = new PostOffice();
		ISubscribe zhangsan = new ZhangSan("张三", "《南方周末》");
		ISubscribe lisi = new Lisi("李四", "《新京报》");
		ISubscribe wangwu = new Wangwu("王五", "《南方都市报》");

		//开始订阅报纸
		zhangsan.registered(mPostOffice);
		lisi.registered(mPostOffice);
		wangwu.registered(mPostOffice);

		//邮局收到报纸,开始发放
		mPostOffice.getNewsPaper(true);

		//李四退订
		lisi.unregistered(mPostOffice);
		//邮局收到报纸,开始发放
		mPostOffice.getNewsPaper(true);
	}

打印结果:

    

我是张三,我收到我订阅的《南方周末》
我是李四,我收到我订阅的《新京报》
我是王五,我收到我订阅的《南方都市报》
我是张三,我收到我订阅的《南方周末》
我是王五,我收到我订阅的《南方都市报》

这里只是提供了一个简单的例子,而且代码你会发现有冗余,三个订阅者类,几乎是一样,因为没有在类里面添加他们独自的属性,可以用一个Person类来替代。这里写这三个类是为了显示清晰。

        关于五月18号的例子,观察者没必要有注册和取消注册的方法。他们的方法的实现也是调用的被观察者的注册和取消注册,不如直接使用被观察者的方法。

         在Android中,button.setOnclickListener()这个方式是比较常见的观察者模式:当然,众所周知,onClick是著名的回调方法,在这里不会研究回调,不用太在意。

看下代码:

Button button1 = (Button)findViewById(R.id.button1);
        Button button2 = (Button)findViewById(R.id.button2);
        Button button3 = (Button)findViewById(R.id.button3);
        button1.setOnClickListener(this);
        button2.setOnClickListener(this);
        button3.setOnClickListener(this);
    }

	@Override
	public void onClick(View v) {
		// TODO Auto-generated method stub
		switch(v.getId()) {
		case R.id.button1 :
//			do some thing
		case R.id.button2 :
//			do some thing
		case R.id.button3 :
//			do some thing
		}
	}

先button.setOnclickListener()进行注册。相当于邮局中的lisi.registered(mPostOffice),此处错误,应该是第一例中的postOffice.registered(person)。onclick响应事件相当于邮局中的

public void sendNewsPaper() {
		for(ISubscribe subscribe : SubscribeList) {
			subscribe.getNewsPaper();
		}
	}

这个中的subscribe.getNewsPaper()这个方法。View.onClickListener相当于邮局。View也就是button是我们的订阅者,也就相当于张三。我们也可以在getNewsPaper中加一些自己的switch判断。

       最后,如果我们的邮局要实现类似button这样的回调这么实现呢?

ISubscribe isubscribe;

	@Override
	public void getNewsPaper() {
		// TODO Auto-generated method stub
		System.out.println("我是"+getName()+",我收到我订阅的"+getNewsPaperName());
		isubscribe.getNewsPaper();
	}

在这里,弄的有点绕,其实可以另起一个接口。不知道是否能够明白,不明白欢迎留言探讨。谢谢,今天就到此,周末愉快!


浅学设计模式之观察者<Observer>模式及在android中的应用:目前有4 条留言

  1. 4楼
    lidaasky:

    [e01]

    2012-05-18 23:42 [回复]
  2. [reply]lidaasky[/reply]
    哈哈,谢谢,你的鼓励是我的动力。哈哈

    2012-05-18 23:45 [回复]
  3. 板凳
    whentanking:

    ISubscribe 接口有一个实现就够了吧,然后让三个人去调用不是更好么。。

    2012-05-31 15:26 [回复]
  4. [reply]whentanking[/reply]
    其实我这个例子写的很不好,打算做一个修改。

    2012-05-31 15:28 [回复]

发表评论


QQ群互动

Linux系统与内核学习群:194051772

WP建站技术学习交流群:194062106

魔豆之路QR

魔豆的Linux内核之路

魔豆的Linux内核之路

优秀工程师当看优秀书籍

优秀程序员,要看优秀书!

赞助商广告

友荐云推荐