定义
观察者模式(又叫订阅-发布模式):定义了一种一对多的关系,让多个观察者对象同时监听一个主题。当主题的状态发生变化时,会通知多个观察者,使他们能够自动更新自己。
类图
分析:
观察者模式包括抽象主题、具体主题、抽象观察者、具体观察者。主题也可叫做被观察者、通知者。
抽象主题:一个接口或者抽象类,定义了主题的行为,包括添加主题(attach())、移除主题(detach())、通知观察者(Notify()),如果是抽象类的话可以去实现attach()、detach()、Notify()行为。抽象主题由具体主题来实现。
具体主题:实现或继承了抽象主题。会在主题发生变化时,会遍历注册在此主题上的观察者,调用观察者的update()方法来让观察者更新自己的状态。
抽象观察者:一个借口或抽象类。为所有的具体观察者定义了udpate()方法,也就主题调用的更新方法。
具体观察者:实现或继承了抽象观察者。使用update()方法来获取主题的状态。
观察者模式很好的体现了倒置依赖原则。主题依赖抽象的观察者,而不依赖具体的观察者,实现了主题和观察者的松耦合。
观察者中的推数据和拉数据
当主题发生变化时,主题可以向观察者推数据,通过遍历主题持有的观察者列表,调用每个主题的update()方法实现(数据可以通过update()方法的参数传递)。观察者也可以从主题拉数据,观察者维护关心的主题的引用,调用主题的方法或字段来获取数据。我们可以发现推和拉数据的两个区别:1,推数据是主题维护一个观察者列表。拉数据是观察者维护一个感兴趣的主题。2,推数据是主题更新状态发生变化可以及时通知观察者。拉数据是观察者感兴趣、用到的时候才去主动获取数据。还有就是我们可以将者两种方式相结合,告诉观察者数据已经备好(推数据),观察者收到通知,去主题上取数据(拉数据)。
观察者模式的实现
按照我们的UML类图来实现观察者模式。
/**
* 抽象主题
* @author hsoft3
*
*/
public abstract class Subject {
private List<Observer> list = new ArrayList<Observer>();
public void notifyObserver(){
for(Observer obs:list){
obs.update();
}
}
public void addObserver(Observer obs){
list.add(obs);
}
public boolean removeObserver(Subject sub){
return list.remove(sub);
}
/**
* 获取主题状态
* @return
*/
public abstract String getSubjectState();
/**
* 设置主题状态
* @param state
*/
protected abstract void setSubjectState(String state);
}
/**
* 抽象观察者
* @author hsoft3
*
*/
public abstract class Observer {
private String name;
private Subject sub;
public Observer(String name, Subject sub){
this.name = name;
this.sub = sub;
//sub.addObserver(this);
}
public Subject getSubject(){
return this.sub;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public abstract void update();
}
/**
* 具体主题
* @author hsoft3
*
*/
public class ConcreteSubject extends Subject {
private String subjectState;
@Override
public String getSubjectState() {
// TODO Auto-generated method stub
return subjectState;
}
@Override
protected void setSubjectState(String state) {
// TODO Auto-generated method stub
this.subjectState=state;
}
}
/**
* 具体观察者
* @author hsoft3
*
*/
public class ConcreteObserver extends Observer {
public ConcreteObserver(String name, Subject sub) {
super(name, sub);
}
@Override
public void update() {
String str = String.format("观察者%s的状态是%s", super.getName(), super.getSubject().getSubjectState());
System.out.println(str);
}
}
总结
优点
1,很好的体现了依赖倒置原则。
2,遵守了开闭原则。
缺点
1,虽然使用了抽象主题和抽象观察者达到了低耦合的目的,但是抽象主题还是依赖了抽象观察者。ps:这个问题可以通过委托和事件来解决。
2,观察者过多或调用观察者的update()方法时间过长,将所有的观察者通知一遍会花费很多时间。
使用场合
1,当一个对象的改变需要改变其它对象时,而且它不知道具体有多少对象有待改变。
2,一个抽象模型有两个方面,其中一方面依赖于另一方面,这时用观察者模式可以将两者封装在独立的对象中使他们各自独立地改变和复用。
ps:知道这句话体现的是依赖倒转原则,但是不太理解这句话。