建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应做出反应。在此,发生改变的对象称为观察目标,而被通知的对象称为观察者,一个观察目标可以对应多个观察者,而且这些观察者之间没有相互联系,可以根据需要增加和删除观察者,使得系统更易于扩展,这就是观察者模式的模式动机。
观察者模式(Observer Pattern):定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。观察者模式又叫做发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。
观察者模式是一种对象行为型模式。
例子:Sunny软件公司欲开发一款实时在线股票软件,该软件需提供如下功能:当股票购买者所购买的
某支股票价格变化幅度达到5%时,系统将自动发送通知(包括新价格)给购买该股票的所有股民。
试使用观察者模式设计并实现该系统。
分析:目标:股票。观察者:股民
实现方式:单向联动观察者模式
package cn.limbo.design_patterns.observer.one_way_linkage;
import java.util.ArrayList;
/**
* 抽象目标类
* Created by limbo on 2016/12/13.
*/
public abstract class Subject {
protected String name;
protected ArrayList observers = new ArrayList<>();
public Subject(String name) {
this.name = name;
}
//添加
public void attach (Observer observer) {
System.out.println(observer.getName() + "买入这支股票");
observers.add(observer);
}
//删除
public void detach(Observer observer){
System.out.println(observer.getName() + "抛出这支股票");
observers.remove(observer);
}
//通知观察者
public abstract void notifyObservers(String state);
}
Observer.java
package cn.limbo.design_patterns.observer.one_way_linkage;
/**
* 抽象观察者类
* Created by limbo on 2016/12/13.
*/
public abstract class Observer {
private String name;
public Observer(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//通知后的响应函数
public abstract void doSomething(String state);
}
Stock.java
package cn.limbo.design_patterns.observer.one_way_linkage.concrete_subject;
import cn.limbo.design_patterns.observer.one_way_linkage.Observer;
import cn.limbo.design_patterns.observer.one_way_linkage.Subject;
/**
* 具体目标类:股票类,实现了抽象目标类
* Created by limbo on 2016/12/13.
*/
public class Stock extends Subject {
public Stock(String name) {
super(name);
}
@Override
public void notifyObservers(String state) {
for(Observer o : this.observers){
o.doSomething(state);
}
}
}
Investor.java
package cn.limbo.design_patterns.observer.one_way_linkage.concrete_observer;
import cn.limbo.design_patterns.observer.one_way_linkage.Observer;
/**
* Created by limbo on 2016/12/13.
*/
public class Investor extends Observer {
public Investor(String name) {
super(name);
}
@Override
public void doSomething(String state) {
System.out.println(this.getName() + "发现股票正在" + state);
if(state.equals("上涨")){
System.out.println("他准备抛出");
}else if(state.equals("下跌")){
System.out.println("他准备买入");
}
}
}
测试类:
@Test
public void oneWayLinkage() {
Subject stock1 = new Stock("A股");
Observer o1, o2, o3;
o1 = new Investor("张三");
o2 = new Investor("李四");
o3 = new Investor("王五");
stock1.attach(o1);
stock1.attach(o2);
stock1.attach(o3);
stock1.notifyObservers("上涨");
}
例子:
Sunny软件公司欲开发一款多人联机对战游戏(类似魔兽世界、星际争霸等游戏),在该游戏中,
多个玩家可以加入同一战队组成联盟,当战队中某一成员受到敌人攻击时将给所有其他盟友发送通知,
盟友收到通知后将作出响应。
分析:
这个例子里面我们需要抽象出一个通知中心的抽象类,然后这里存在了两个联动关系,玩家->通知中心
通知中心->玩家,玩家和通知中心既是观察对象也是观察者
实现模式:
双向联动观察者模式
package cn.limbo.design_patterns.observer.two_way_linkage;
import java.util.ArrayList;
/**
* 抽象观察对象
* Created by limbo on 2016/12/13.
*/
public abstract class Subject {
protected String name;
public Subject(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//添加观察者
public abstract void attach(Observer observer) throws InvalidOptionException;
//删除观察者
public abstract void detach(Observer observer) throws InvalidOptionException;
//通知观察者
public abstract void notifyObserver(Message msg);
}
Observer.java
package cn.limbo.design_patterns.observer.two_way_linkage;
/**
* 抽象观察者
* Created by limbo on 2016/12/13.
*/
public interface Observer {
//接收到通知之后的响应函数
public void response(Message msg);
public String getName();
}
package cn.limbo.design_patterns.observer.two_way_linkage;
import java.util.ArrayList;
/**
* 消息控制中心,用于接受消息和发送消息
* 既是观察者也是观察目标
* Created by limbo on 2016/12/13.
*/
public class MessageController extends Subject implements Observer {
private ArrayList players;
public MessageController(String name) {
super(name);
players = new ArrayList<>();
}
@Override
public void attach(Observer observer) throws InvalidOptionException {
System.out.println(observer.getName() + "加入游戏");
this.players.add((Player) observer);
}
@Override
public void detach(Observer observer) throws InvalidOptionException {
System.out.println(observer.getName() + "退出游戏");
this.players.remove(observer);
}
@Override
public void notifyObserver(Message msg) {
for(Player p : this.players){
if(!p.getName().equals(msg.getPlayerNameBeingAttack())){
p.response(msg);
}
}
}
//对玩家的求救信息作出反应
@Override
public void response(Message msg) {
this.notifyObserver(msg);
}
}
package cn.limbo.design_patterns.observer.two_way_linkage;
/**
* 玩家
* 可以发布求救信息也可以实施救援
* 既是观察者也是观察对象
* Created by limbo on 2016/12/13.
*/
public class Player extends Subject implements Observer{
private MessageController mc;
public Player(String name,MessageController mc) {
super(name);
this.mc = mc;
}
@Override
public void attach(Observer observer) throws InvalidOptionException {
throw new InvalidOptionException("没有该方法");
}
@Override
public void detach(Observer observer) throws InvalidOptionException {
throw new InvalidOptionException("没有该方法");
}
//盟友受到攻击,作出回应
@Override
public void response(Message msg) {
System.out.println(this.name + "收到" + msg.getPlayerNameBeingAttack() + "的求救消息");
System.out.println(this.name + "马上前去支援");
}
//自己受到攻击,向通知中心发送求助信息
@Override
public void notifyObserver(Message msg) {
System.out.println(this.name + " : "+msg.getContent());
this.mc.response(msg);
}
//广播求救信息
public void broadcast(){
Message msg = new Message(this.name,"遭受攻击");
this.notifyObserver(msg);
}
}
package cn.limbo.design_patterns.observer.two_way_linkage;
/**
* 求救消息类
* Created by limbo on 2016/12/13.
*/
public class Message {
private String playerNameBeingAttack; //正在遭受攻击的玩家
private String content; //消息内容
public Message(String playerNameBeingAttack, String content) {
this.playerNameBeingAttack = playerNameBeingAttack;
this.content = content;
}
public String getPlayerNameBeingAttack() {
return playerNameBeingAttack;
}
public void setPlayerNameBeingAttack(String playerNameBeingAttack) {
this.playerNameBeingAttack = playerNameBeingAttack;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
InvalidOptionException.java
package cn.limbo.design_patterns.observer.two_way_linkage;
/**
* 如果目标无法访问该方法,抛出一个自定义的异常
* Created by limbo on 2016/12/13.
*/
public class InvalidOptionException extends Exception {
public InvalidOptionException(String message) {
super(message);
}
}