观察者模式
1)概述
1.定义
定义对象之间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。
观察者模式的别名包括发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。
2.作用
建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应作出反应。
在观察者模式中,发生改变的对象称为观察目标,而被通知的对象称为观察者,一个观察目标可以对应多个观察者,而且这些观察者之间可以没有任何相互联系,可以根据需要增加和删除观察者,使得系统更易于扩展。
3.结构图
4.角色
Subject(目标):目标又称为主题,指被观察的对象,在目标中定义了一个观察者集合,一个观察目标可以接受任意数量的观察者来观察,它提供一系列方法来增加和删除观察者对象,同时它定义了通知方法notify(),目标类可以是接口,也可以是抽象类或具体类。
ConcreteSubject(具体目标):具体目标是目标类的子类,通常它包含有经常发生改变的数据,当它的状态发生改变时,向它的各个观察者发出通知;同时它还实现了在目标类中定义的抽象业务逻辑方法,如果无须扩展目标类,则具体目标类可以省略。
Observer(观察者):观察者将对观察目标的改变做出反应,观察者一般定义为接口,该接口声明了更新数据的方法update(),因此又称为抽象观察者。
ConcreteObserver(具体观察者):在具体观察者中维护一个指向具体目标对象的引用,它存储具体观察者的有关状态,这些状态需要和具体目标的状态保持一致;它实现了在抽象观察者Observer中定义的update()方法,通常在实现时,可以调用具体目标类的attach()方法将自己添加到目标类的集合中或通过detach()方法将自己从目标类的集合中删除。
5.代码实现
观察者模式描述了如何建立对象与对象之间的依赖关系,以及如何构造满足这种需求的系统。
抽象目标类Subject
import java.util.*;
abstract class Subject {
//定义一个观察者集合用于存储所有观察者对象
protected ArrayList observers<Observer> = new ArrayList();
//注册方法,用于向观察者集合中增加一个观察者
public void attach(Observer observer) {
observers.add(observer);
}
//注销方法,用于在观察者集合中删除一个观察者
public void detach(Observer observer) {
observers.remove(observer);
}
//声明抽象通知方法
public abstract void notify();
}
具体目标类ConcreteSubject
public class ConcreteSubject extends Subject {
//实现通知方法
public void notify() {
//遍历观察者集合,调用每一个观察者的响应方法
for(Object obs:observers) {
((Observer)obs).update();
}
}
}
抽象观察者Observer
interface Observer {
//声明响应方法
public void update();
}
具体观察者ConcreteObserver
public class ConcreteObserver implements Observer {
//实现响应方法
public void update() {
//具体响应代码
}
}
6.注意
在复杂的情况下,具体观察者类ConcreteObserver的update()方法在执行时需要使用到具体目标类ConcreteSubject中的状态(属性),因此在ConcreteObserver与ConcreteSubject之间有时候还存在关联或依赖关系,在ConcreteObserver中定义一个ConcreteSubject实例,通过该实例获取存储在ConcreteSubject中的状态。
2)完整解决方案
1.结构图
AllyControlCenter充当抽象目标类,ConcreteAllyControlCenter充当具体目标类,Observer充当抽象观察者,Player充当具体观察者。
2.代码实现
import java.util.*;
//抽象观察类
interface Observer {
public String getName();
public void setName(String name);
public void help(); //声明支援盟友方法
public void beAttacked(AllyControlCenter acc); //声明遭受攻击方法
}
//战队成员类:具体观察者类
class Player implements Observer {
private String name;
public Player(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
//支援盟友方法的实现
public void help() {
System.out.println("坚持住," + this.name + "来救你!");
}
//遭受攻击方法的实现,当遭受攻击时将调用战队控制中心类的通知方法notifyObserver()来通知盟友
public void beAttacked(AllyControlCenter acc) {
System.out.println(this.name + "被攻击!");
acc.notifyObserver(name);
}
}
//战队控制中心类:目标类
abstract class AllyControlCenter {
protected String allyName; //战队名称
protected ArrayList<Observer> players = new ArrayList<Observer>(); //定义一个集合用于存储战队成员
public void setAllyName(String allyName) {
this.allyName = allyName;
}
public String getAllyName() {
return this.allyName;
}
//注册方法
public void join(Observer obs) {
System.out.println(obs.getName() + "加入" + this.allyName + "战队!");
players.add(obs);
}
//注销方法
public void quit(Observer obs) {
System.out.println(obs.getName() + "退出" + this.allyName + "战队!");
players.remove(obs);
}
//声明抽象通知方法
public abstract void notifyObserver(String name);
}
//具体战队控制中心类:具体目标类
class ConcreteAllyControlCenter extends AllyControlCenter {
public ConcreteAllyControlCenter(String allyName) {
System.out.println(allyName + "战队组建成功!");
System.out.println("----------------------------");
this.allyName = allyName;
}
//实现通知方法
public void notifyObserver(String name) {
System.out.println(this.allyName + "战队紧急通知,盟友" + name + "遭受敌人攻击!");
//遍历观察者集合,调用每一个盟友(自己除外)的支援方法
for(Object obs : players) {
if (!((Observer)obs).getName().equalsIgnoreCase(name)) {
((Observer)obs).help();
}
}
}
}
客户端类
class Client {
public static void main(String[] args) {
//定义观察目标对象
AllyControlCenter acc;
acc = new ConcreteAllyControlCenter("金庸群侠");
//定义四个观察者对象
Observer player1,player2,player3,player4;
player1 = new Player("杨过");
acc.join(player1);
player2 = new Player("令狐冲");
acc.join(player2);
player3 = new Player("张无忌");
acc.join(player3);
player4 = new Player("段誉");
acc.join(player4);
//某成员遭受攻击
player1.beAttacked(acc);
}
}