观察者模式
意图
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于他的对象都得到通知并被自动更新
适用性
1、当一个抽象模型有两个方面,其中一个依赖另外一个
2、当对一个对象的改变需要同时改变其他对象,而不知道具体有多少对吸纳个有待改变
3、当一个对象那个必须通知其他对象,而它又不能假定其他对象是谁,换言之,你不希望这些对象时紧密耦合的
结构
Subject
目标知道他的观察者。可以有任意多的观察者观察同一个目标
提供注册和删除观察者对象的接口
Observer
为那些在目标发生变化时需要被通知的对象定义一个更新接口
ConcreteSubject
将有关状态存入各ConcreteObserver对象
当他的状态发生改变时,向他的各个观察者发出通知
ConcreteObserver
维护一个指向ConcreteSubject对象的引用
存储有关状态,这些状态应与目标状态保持一致
是想Observer的更新接口以使自身状态与目标状态保持一致
实现
现在,我们有一个游戏,一共有5个角色,当前是其中有一个角色被攻击的时候,通知其他角色,并且其他角色对其作出反应
package observer;
/**
* @Author fitz.bai
* @Date 2018/9/6 20:50
*/
public interface Observer {
String getName();
void setName(String name);
void help(); //update();
void beAttacked(ControlCenter controlCenter);
}
package observer;
/**
* @Author fitz.bai
* @Date 2018/9/6 21:03
*/
public class Player implements Observer {
private String name;
public Player(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public void setName(String name) {
this.name = name;
}
@Override
public void help() {
System.out.println(this.name + "--lei了,他lei了");
}
@Override
public void beAttacked(ControlCenter controlCenter) {
System.out.println(this.name + "正受到攻击!");
controlCenter.notifyObservers(name);
}
}
package observer;
import java.util.ArrayList;
import java.util.List;
/**
* @Author fitz.bai
* @Date 2018/9/6 20:52
*/
abstract class ControlCenter {
protected String name;
protected List<Observer> players = new ArrayList<>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void attach(Observer observer) {
System.out.println(observer.getName() + "加入了游戏!");
players.add(observer);
}
public void detach(Observer observer) {
System.out.println(observer.getName() + ",你走吧!我们能赢!");
players.remove(observer);
}
public abstract void notifyObservers(String name);
}
package observer;
/**
* @Author fitz.bai
* @Date 2018/9/6 20:58
*/
public class LGDControlCenter extends ControlCenter {
@Override
public void notifyObservers(String name) {
System.out.println("队友呢?队友呢?队友呢?队友呢?队友呢?");
for (Observer o : players) {
if (!o.getName().equalsIgnoreCase(name)) {
o.help();
}
}
}
}
package observer;
import iterator.ConcreteList;
/**
* @Author fitz.bai
* @Date 2018/9/6 20:50
*/
public class Client {
public static void main(String[] args) {
ControlCenter controlCenter = new LGDControlCenter();
Observer player1, player2, player3, player4, player5;
player1 =new Player("船长");
controlCenter.attach(player1);
player2 =new Player("PA");
controlCenter.attach(player2);
player3 =new Player("SPE");
controlCenter.attach(player3);
player4 =new Player("AM");
controlCenter.attach(player4);
player5 =new Player("WR");
controlCenter.attach(player5);
System.out.println("=============有玩家推出了游戏===================");
player1.beAttacked(controlCenter);
controlCenter.detach(player1);
player2.beAttacked(controlCenter);
}
}
/**船长加入了游戏!
PA加入了游戏!
SPE加入了游戏!
AM加入了游戏!
WR加入了游戏!
船长正受到攻击!
队友呢?队友呢?队友呢?队友呢?队友呢?
PA--lei了,他lei了
SPE--lei了,他lei了
AM--lei了,他lei了
WR--lei了,他lei了
=============有玩家退出了游戏===================
船长,你走吧!我们能赢!
PA正受到攻击!
队友呢?队友呢?队友呢?队友呢?队友呢?
SPE--lei了,他lei了
AM--lei了,他lei了
WR--lei了,他lei了
*/
总结
其实观察者模式很简单,可以简单理解为,有一个对象关联着其他未知数目的对象,当这个对象发生变化时,通知其他对象,其他对象那个做出相应反应
加入对象A为被观察的对象,它维持着一众仰慕者他的对象B,当对象A发生变化时,他会调用内部的notify方法通知他的仰慕者们,在每一个对象B中呢,维护一个指向对象A的引用,当A变化时就调用update方法作出反应
MVC
MVC(Model-View-Controller)架构中也应用了观察者模式,其中模型(Model)可对应于观察者模式中的观察目标,而视图(View)对应于观察者,控制器(Controller)可充当两者之间的中介者。当模型层的数据发生改变时,视图层将自动改变其显示内容。
优点
1、可以实现表示层和数据逻辑层的分离,定义了稳定的消息更新传递机制,并抽象了更新接口,使得可以有各种各样不同的表示层充当具体观察者角色
2、 观察者模式在观察目标和观察者之间建立一个抽象的耦合。观察目标只需要维持一个抽象观察者的集合,无须了解其具体观察者。由于观察目标和观察者没有紧密地耦合在一起,因此它们可以属于不同的抽象化层次
3、观察者模式支持广播通信,观察目标会向所有已注册的观察者对象发送通知,简化了一对多系统设计的难度
(4) 增加新的具体观察者无须修改原有系统代码,在具体观察者与观察目标之间不存在关联关系的情况下,增加新的观察目标也很方便,满足“开闭原则”的要求
缺点
1、如果一个观察目标对象有很多直接和间接观察者,将所有的观察者都通知到会花费很多时间。
2、如果在观察者和观察目标之间存在循环依赖,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
3、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。