【设计模式】观察者模式(Observer Pattern)和“发布者-订阅者”模式(Pub-Sub Pattern)

1,观察者模式(Observer Pattern)

又名发布(publish )-订阅(Subscribe)模式、模型-视图(View)模式、源-收听者(Listener)模式、从属者模式。

1)概念

观察者模式定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
观察者模式属于行为型模式,行为型模式关注的是对象之间的通讯,观察者模式就是观察者和被观察者之间的通讯。
在这里插入图片描述

2)用途

通常被用来实现事件处理系统。
在实际应用中,如果一个事物改变了需要通知另一个事物此时就需要使用观察者来监控。

事件通知实现的基本思想来源于观察者模式。(与Swing一样,都使用观察者模式)

3)实现

①Java内置的观察者模式。

是一个类。不灵活。

②自己实现

UML图
i>新建接口观察者。
ii>新建接口主题(被观察者)。
iii>观察者类实现观察者接口。(观察者可能有很多)
iv>被观察者实现主题接口。
v>实例化被观察者(单例) -> 实例观察者 -> 被观察者添加观察者 -> 数据变化后,被观察者调用notifyObserver()方法,通知所有观察者数据改变 -> 观察者接收到数据改变信息后处理。

4)注意

①不要依赖于观察者被通知的次序。这违反了松耦合原则。
②被观察者和观察者之间松耦合。一个被观察者(主题)可以有很多观察者。
③被观察者(主题)是真正拥有数据的人,观察者是主题的依赖者,在数据变化时更新,这样可以得到更干净的OO设计。
④针对具体实现编程,回导致我们以后在增加或删除布告板时,必须修改程序。观察者模式,将改变的地方分离并封装,符合OO设计原则。

5)优缺点

优:
实现观察者和被观察者之间的松耦合
支持一对多通知,只要是注册过的观察者都能收到通知
缺:
可能需要调用多次都能收到所需要的全部数据
也可能会有多余的数据

2,“发布者-订阅者”模式(Pub-Sub Pattern)

1)概念

发布者的消息不会直接发送给订阅者,通过第三个组件(代理、消息代理或事件总线)过滤发布者的消息再分发给订阅者。它过滤所有传入的消息并相应地分发它们。发布者和订阅者不知道彼此的存在,发布者和订阅者只知道中间代理的存在。换句话说,pub-sub是用于在不同系统组件之间传递消息的模式,而这些组件不知道关于彼此身份的任何信息。
在这里插入图片描述

2)与观察者模式的对比

观察者模式(Observer Pattern)和发布(publish )-订阅(Subscribe)模式是2种模式,相似却不同。24种基本的设计模式并没有发布-订阅模式,发布订阅模式属于并发型模式。也可以理解为发布订阅模式是观察者模式的一种变体。
这两种模式主要区别如下:
在这里插入图片描述

  1. 在Observer模式中,Observers知道Subject,同时Subject还保留了Observers的记录。然而,在发布者/订阅者中,发布者和订阅者不需要彼此了解。他们只是在消息队列或代理的帮助下进行通信。
  2. 在Publisher / Subscriber模式中,组件是松散耦合的,而不是Observer模式。
  3. 观察者模式主要以同步方式实现,即当某些事件发生时,Subject调用其所有观察者的适当方法。发布者/订阅者在大多情况下是异步方式(使用消息队列)。
  4. 观察者模式需要在单个应用程序地址空间中实现。另一方面,发布者/订阅者模式更像是跨应用程序模式。

3)举例

kafka

3,Android中Observer Pattern的应用

观察者模式在android系统中的应用还是相当广泛的,如BroadcastReceiver、各种回调机制的实现(Activity的生命周期、按钮的点击事件、线程的run()方法)、Adapter 数据的更新。
比较知名的观察者模式的开源框架:EventBus、AndroidEventBus、otto(也是EventBus的一种实现)。
观察者模式看起来很高大上,其实说白了就是一个类维护了另一个类的一个集合,并通过这个集合绑定解绑或调用另一个类的方法,只不过,在设计底层框架时候,利用了多态的特性抽象出了接口和抽象类,以便适用于各种场合。
我们不能盲目的套用设计模式,因为多态会增加运行时开销,需权衡利弊,在使用。

1)Adapter数据的更新

① BaseAdapter.java(抽象的被观察者)

public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
    // 数据集观察者
    private final DataSetObservable mDataSetObservable = new DataSetObservable();

    // 代码省略

    public void registerDataSetObserver(DataSetObserver observer) {
        mDataSetObservable.registerObserver(observer);
    }

    public void unregisterDataSetObserver(DataSetObserver observer) {
        mDataSetObservable.unregisterObserver(observer);
    }

    /**
     * Notifies the attached observers that the underlying data has been changed
     * and any View reflecting the data set should refresh itself.
     * 当数据集用变化时通知所有观察者
     */
    public void notifyDataSetChanged() {
        mDataSetObservable.notifyChanged();
    }
}

②MyAdapter(具体的被观察者)

ListView.java

public class ListView {
    ...代码省略
    @Override
    public void setAdapter(ListAdapter adapter) {
        ...代码省略
        mDataSetObserver = new AdapterDataSetObserver();
        mAdapter.registerDataSetObserver(mDataSetObserver);
        ...代码省略
    }
    ...代码省略
}

通过listView.setAdapter(myAdapter);将 AdapterDataSetObserver 注册到 Adapter 的DataSetObservable中。
在我们往ListView添加数据后,myAdapter.notifyDataSetChanged(),通知ListView更新数据。

③DataSetObservable(抽象观察者)

public class DataSetObservable extends Observable<DataSetObserver> {
    /**
     * Invokes onChanged on each observer. Called when the data set being observed has
     * changed, and which when read contains the new state of the data.
     */
    public void notifyChanged() {
        synchronized(mObservers) {
            // 调用所有观察者的onChanged方式
            for (int i = mObservers.size() - 1; i >= 0; i--) {
                mObservers.get(i).onChanged();
            }
        }
    }

}

当数据发生变化时候,notifyDataSetChanged中会调用mDataSetObservable.notifyChanged()方法,此时会遍历所有观察者,并且调用它们的onChanged方法。

④AdapterDataSetObserver (具体的观察者)

class AdapterDataSetObserver extends DataSetObserver {

        private Parcelable mInstanceState = null;
        // 调用Adapter的notifyDataSetChanged的时候会调用所有观察者的onChanged方法,核心实现就在这里
        @Override
        public void onChanged() {
            mDataChanged = true;
            mOldItemCount = mItemCount;
            // 获取Adapter中数据的数量
            mItemCount = getAdapter().getCount();

            // Detect the case where a cursor that was previously invalidated has
            // been repopulated with new data.
            if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null
                    && mOldItemCount == 0 && mItemCount > 0) {
                AdapterView.this.onRestoreInstanceState(mInstanceState);
                mInstanceState = null;
            } else {
                rememberSyncState();
            }
            checkFocus();
            // 重新布局ListView、GridView等AdapterView组件
            requestLayout();
        }

        // 代码省略

        public void clearSavedState() {
            mInstanceState = null;
        }
    }

2)ContentObserver(内容观察者)

①概念

内容观察者,目的是观察(捕捉)特定Uri引起的数据库的变化,继而做一些相应的处理,它类似于数据库技术中的触发器(Trigger),当 ContentObserver 所观察的Uri发生变化时,便会触发它。

②观察特定Uri的步骤如下

创建我们特定的 ContentObserver 派生类,必须重载父类构造方法,必须重载 onChange() 方法去处理回调后的功能实现。
利用 context.getContentResolover() 获得 ContentResolove 对象,接着调用 registerContentObserver() 方法去注册内容观察者。
由于 ContentObserver 的生命周期不同步于 Activity 和 Service 等,因此,在不需要时,需要手动的调用 unregisterContentObserver() 去取消注册。

4、Observer Pattern demo

1)通过观察者监控网络的变换(根据连接的服务器IP的不同做不同的操作)

/**
 * 被观察者
 * @author luo
 */
public interface Watched_net {

	/**
	 * 增加观察者
	 * @param watcher_net
	 */
	public void add(Watcher_net watcher_net);
	/**
	 * 移除观察者权限
	 * @param watcher_net
	 */
	public void remove(Watcher_net watcher_net);
	/**
	 * 可以实现行为并向观察者传输信息
	 * @param s
	 */
	public void notifyWatcher(String s);
}
public interface Watcher_net {

    /**
     * 用来获取更新信息接收的方法  
     * 观察到新的变化后,做什么动作
     * @param content
     */
    public void updateNotify(String s); 
}
/**
 * 被观察者 观察是否有网络切换
 * 
 * @author luo
 *
 */
public class Net_switch implements Watched_net {

	/** use to Package Watcher_net */
	private List<Watcher_net> list = new ArrayList<>();
	/** 网络切换标志,内网为"i",外网为"e"; */
	private String Net_flag = "";

	final private static Net_switch net_switch = new Net_switch();

	public static Net_switch getInstance() {
		return net_switch;
	}

	private Net_switch() {

	}

	public String getNet_flag() {
		return Net_flag;
	}

	public void setNet_flag(String net_flag) {
		Net_flag = net_flag;
	}

	@Override
	public void add(Watcher_net watcher_net) {
		list.add(watcher_net);
	}

	@Override
	public void remove(Watcher_net watcher_net) {
			list.remove(watcher_net);
	}

	/**
	 * 通知所有的观察者有新的变化
	 */
	@Override
	public void notifyWatcher(String s) {

			for (Watcher_net watcher : list) {
				watcher.updateNotify(s);
			}
	}
}
	private void setWatcher() {
			Net_switch.getInstance().remove(GFragment.getInstance());
			Net_switch.getInstance().remove(CFragment.getInstance());
			Net_switch.getInstance().add(T.getInstance());
	}

2)RecyclerView的item点击回调

3)MVP

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值