一.基本概念:当对象间存在一对多关系时,则使用观察者模式(Observer Pattern)。比如,当一个对象被修改时,则会自动通知它的依赖对象。观察者模式属于行为型模式。
二。介绍:
意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
主要解决:一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
何时使用:一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。
如何解决:使用面向对象技术,可以将这种依赖关系弱化。
关键代码:在抽象类里有一个 ArrayList 存放观察者们。
应用实例: 1、拍卖的时候,拍卖师观察最高标价,然后通知给其他竞价者竞价。 2、西游记里面悟空请求菩萨降服红孩儿,菩萨洒了一地水招来一个老乌龟,这个乌龟就是观察者,他观察菩萨洒水这个动作。
优点: 1、观察者和被观察者是抽象耦合的。 2、建立一套触发机制。
缺点: 1、如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。 2、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。 3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
使用场景:
- 一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
- 一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
- 一个对象必须通知其他对象,而并不知道这些对象是谁。
- 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。
注意事项: 1、JAVA 中已经有了对观察者模式的支持类。 2、避免循环引用。 3、如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。
三.下面以明星粉丝为例(参考网上的),
上面这位明星在新浪微博上发了一条动态,说他会唱、跳rap等等。然后他的粉丝就都知道了。从这个例子中我们可以看到,这里包含了两种人,第一种是明星,第二个是粉丝。转化为设计模式中的语言就是主题和观察者。
我们的明星的微博就相当于与一个主题,粉丝就是观察者,随时观察明星的动态。不过明星有权利让你关注,也有权利把你拉黑。现在我们从类图的角度来看一下:
从上面我们可以看到,这里面包含了两大类(主题和观察者)一共四个角色:
(1)Subject:抽象主题,他把所有观察者对象保存在一个集合里,可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。意思就是明星把所有的粉丝都保存在一个账号里面,粉丝数量不限,可以新增粉丝也可以拉黑粉丝。
(2)ConcreteSubject:具体主题,该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知。意思是我们的明星一有动态,就会把消息给粉丝。
(3)Observer:抽象观察者,是观察者者的抽象类,它定义了一个更新接口,使得在得到主题更改通知时更新自己。这就是我们所有粉丝的抽象。
(4)ConcrereObserver:具体观察者,实现抽象观察者定义的更新接口,以便在得到主题更改通知时更新自身的状态。具体每一个粉丝。
观察者模式还是比较简单的,就是一个粉丝关注明星的实例。现在代码来实现一下。
四、代码实现观察者模式
这个例子我们还是那上面的例子来解释说明。
第一步:定义抽象观察者(Observer):抽象的粉丝
public interface FanObserver {
public void update(String message);
}
第二步:具体的观察者(concreteObserver):具体的粉丝
public class ConcreteFan implements FanObserver{
private String fanName;
public ConcreteFan(String fanName) {
this.fanName=fanName;
}
@Override
public void update(String message) {
System.out.println(fanName+":"+message);
}
}
第三步:定义抽象主题(Subject):抽象明星
public interface MingSubject {
public void addFan(FanObserver fan);
public void delFan(FanObserver fan);
public void notify(String message );
}
第四步:具体主题(ConcreteSubject):具体的明星
import java.util.ArrayList;
import java.util.List;
public class ConcreteMing implements MingSubject{
private List<FanObserver> list = new ArrayList<FanObserver>();
@Override
public void addFan(FanObserver fan) {
list.add(fan);
// TODO Auto-generated method stub
}
@Override
public void delFan(FanObserver fan) {
list.remove(fan);
// TODO Auto-generated method stub
}
@Override
public void notify(String message) {
// TODO Auto-generated method stub
for(FanObserver fan : list) {
fan.update(message);
}
}
}
第五步:测试
public class Test {
public static void main(String[] args) {
ConcreteMing ming = new ConcreteMing();
ConcreteFan fan = new ConcreteFan("zhangsan");
ConcreteFan fan1 = new ConcreteFan("lisi");
ConcreteFan fan2 = new ConcreteFan("wanger");
ming.addFan(fan);
ming.addFan(fan1);
ming.addFan(fan2);
ming.notify("唱,跳,rap");
}
}
结果:
五。如果我们需要通知的不只是粉丝这一类人,还有朋友,亲人等。
第一:加入朋友类
public class ConcreteFri implements FanObserver{
private String fanName;
public ConcreteFri(String fanName) {
this.fanName=fanName;
}
@Override
public void update(String message) {
System.out.println(fanName+":朋友"+message);
}
}
第二:修改测试类
public class Test {
public static void main(String[] args) {
ConcreteMing ming = new ConcreteMing();
//粉丝
ConcreteFan fan = new ConcreteFan("zhangsan");
ConcreteFan fan1 = new ConcreteFan("lisi");
ConcreteFan fan2 = new ConcreteFan("wanger");
//朋友
ConcreteFri fri = new ConcreteFri("cxkfri");
ming.addFan(fan);
ming.addFan(fan1);
ming.addFan(fan2);
ming.addFan(fri);
ming.notify("唱,跳,rap");
}
}
结果: