观察者模式又称为发布—订阅Subscriblem模式。
观察者模式(Obsrever)完美的将观察者和被观察的对象分离开。举个例子,用户界面可以作为一个观察者,业务数据是被观察者,用户界面观察业务数据的变化,发现数据变化后,就显示在界面上。面向对象涉及的一个原则是:单一职责原则。系统中的每个类将重点放在一个功能上,而不是其他方面。一个对象只做一件事,并且将它做好。观察者模式在模块之前划定了清晰的界限,提高了应用程序的可维护性和重用性。
观察者模式定义了对象间的一种一对多的依赖关系,以便一个对象的状态发送变化时,所有依赖于它的对象都得到通知并并自动刷新。
观察者实现模式有很多实现方式,从根本上说,该模式必须包含两个角色:观察者和被观察者对象。在刚才的例子中,业务数据是被观察对象,用户界面是观察者。观察者和被观察者之间存在"观察"的逻辑关联,当被观察者发生改变的时候,可以确保界面和数据之间划清界限,假定应用程序的需求发送变化,需要修改界面的表现,只需要重新构建一个用户界面,业务数据不需要发生变化。
实现观察者模式的时候要注意,观察者和被观察者之间的关系不能体现为类之间的直接调用,否则就将使观察者和被观察者对象之间紧密的耦合起来,从根本上违反面向对象的设计原则。无论是观察者"观察"观察对象,还是被观察者将自己的改变"通知"观察者,都不应该直接调用。
观察者模式分为Push(被观察者将自己的改变"通知"观察者)和Pull(观察者"观察"观察对象)两种实现方式。一般常见的是使Push模式。
实现观察者模式有很多形式,比较直观的一种是使用"注册—通知—撤销注册"的形式。下面的三个图详细的描述了这样一种过程:
观察者
(Observer)将自己注册到被观察者对象(Subject)中,被观察者对象将观察者存放在一个容器(Container)里。
被观察者
被观察者对象发生了某种变化(如图中的SomeChange),从容器中得到所有注册过的观察者,将变化通知观察者。
撤销观察
观察者告诉被观察者要撤销观察、被观察者从容器里将观察者去除。
观察者将自己注册到被观察者的容器中时,被观察者不应该过问观察者的具体类型,而是应该使用观察者的接口。这样的优点是:假定程序中还有别的观察者,那么只要这个观察者也是相同的接口实现即可。一个被观察者可以对应多个观察者,当被观察者发生变化的时候,它可以将消息一一通知给所有的观察者。基于接口,而不是具体的实现——这一点为程序提供了更大的灵活性。
具体例子
下面以项目中遇到的一个非常适合观察者模式的实际应用场景为例来讲解:实际项目中,我们可能 会经常遇到下载的情况。当有多个下载任务在后台下载时,下载任务是被观察者,下载任务列表页面是 观察者对象。
这里使用观察者模式的好处是:
1.让业务逻辑和界面显示分离,实现MVC分层
2.节省资源,让页面不可见时,注销观察者,这样就不再刷新View。
public interface DownloadObserver {
void onDownloadChanged();
}
//观察者对象1
public class DldingJobActivity implements DownloadObserver{
......
@Override
protected void onPause() {
......
//注销观察者
Dld.getInstance().deregisterDownloadObserver(this);
super.onPause();
}
@Override
protected void onResume() {
......
//注册观察者
Dld.getInstance().notifyObservers();
super.onResume();
}
@Override
public void onDownloadChanged() {
//得到通知,更新UI
mHandler.post(mUpdateTimeTask);
}
......
}
//观察者对象2
public class DownloadJobActivity implements DownloadObserver{
......
@Override
protected void onPause() {
......
//注销观察者
Dld.getInstance().deregisterDownloadObserver(this);
super.onPause();
}
@Override
protected void onResume() {
......
//注册观察者
Dld.getInstance().notifyObservers();
super.onResume();
}
@Override
public void onDownloadChanged(){
//得到通知,更新UI
.......
}
......
}
//被观察者对象
public class DownloadJob{
......
public void setDownloadedSize(long downloadedSize) {
if (downloadedSize == mDownloadedSize) {
return;
}
mDownloadedSize = downloadedSize;
int tempProgress = 0;
tempProgress = (int) ((mDownloadedSize * 100) / mTotalSize);
if (tempProgress != mProgress) {
mProgress = tempProgress;
Dld.getInstance().notifyObservers();
}
}
.......
}
public class Dld{
private static Dld instance;
private ArrayList<DownloadObserver> mObservers;
public static Dld getInstance() {
if (instance == null) {
instance = new Dld();
}
return instance;
}
public Dld(){
mObservers = new ArrayList<DownloadObserver>();
}
public synchronized void deregisterDownloadObserver(DownloadObserver observer) {
mObservers.remove(observer);
}
public synchronized void registerDownloadObserver(DownloadObserver observer) {
mObservers.add(observer);
}
public synchronized void notifyObservers() {
for (DownloadObserver observer : mObservers) {
observer.onDownloadChanged(this);
}
}
}
Java里提供了相关的API方便我们实现Observer模式。相关实现如下:
public class MainActivity{
......
private void initNetErrorObserver() {
try {
mNetErrorView = findViewById(R.id.net_error_layout);
NetMonitor.getInstance().addObserver(mNetObserver);
} catch (Exception e) {
}
}
private NetObserver mNetObserver = new NetObserver() {
@Override
public void notify(NetAction action) {
if (mNetErrorView == null)
return;
if (action.isAvailable()) {
mNetErrorView.setVisibility(View.GONE);
} else {
mNetErrorView.setVisibility(View.VISIBLE);
}
}
};
......
}
public class NetMonitor extends BroadcastReceiver{
private final static String TAG = "NetMonitor";
//observable object for the net monitor
private NetObservable observable = null;
//single instance of network monitor
private static NetMonitor instance = null;
public static NetMonitor getInstance(){
if(instance == null){
instance = new NetMonitor();
}
return instance;
}
public void init(Context context){
this.observable = new NetObservable(context);
//filter the network connectivity change action
//broadcast message
IntentFilter filter = new IntentFilter();
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
//register the broadcast receiver
context.registerReceiver(this, filter);
}
public void addObserver(FSNetObserver observer){
this.observable.addObserver(observer);
}
public void delObserver(FSNetObserver observer){
this.observable.deleteObserver(observer);
}
@Override
public void onReceive(Context context, Intent intent) {
this.notifyNetState(context);
}
private void notifyNetState(Context context){
try{
//network state changed, get current network state
NetworkInfo ni = FSDevice.Network.getCurrentActiveNetwork(context);
if(ni != null){
if(!ni.isAvailable()){
this.observable.notifyObservers(new FSNetObserver.NetAction(false, false, false));
}else{
if(ni.getType() == ConnectivityManager.TYPE_WIFI){
this.observable.notifyObservers(new FSNetObserver.NetAction(true, true, true));
}else{
this.observable.notifyObservers(new FSNetObserver.NetAction(true, false, false));
}
}
}else{
this.observable.notifyObservers(new FSNetObserver.NetAction(false, false, false));
}
}catch(Exception e){
FSLogcat.e(TAG, e.getMessage());
}
}
public void destory(){
this.observable.deleteObservers();
}
}
public class NetObservable extends Observable{
private final String TAG = "NetObservable";
//must be application's context
private Context context = null;
public NetObservable(Context context){
super();
this.context = context;
}
@Override
public void addObserver(Observer observer) {
try{
super.addObserver(observer);
NetworkInfo ni = Device.Network.getCurrentActiveNetwork(context);
if(ni != null){
if(!ni.isAvailable()){
observer.update(this, new NetObserver.NetAction(false, false, false));
}else{
if(ni.getType() == ConnectivityManager.TYPE_WIFI){
observer.update(this, new NetObserver.NetAction(true, true, true));
}else{
observer.update(this, new NetObserver.NetAction(true, false, false));
}
}
}else{
observer.update(this, new NetObserver.NetAction(false, false, false));
}
}catch(Exception e){
Logcat.e(TAG, e.getMessage());
}
}
@Override
public void notifyObservers(Object data){
try{
this.setChanged();
super.notifyObservers(data);
}catch(Exception e){
Logcat.e(TAG, e.getMessage());
}
}
}