观察者模式
定义对象之间的一种一对多的依赖关系,使得每当一个对象状态发生改变时,其相关依赖的对象皆得到通知并更新。观察者模式的别名为发布-订阅模式,模型-视图模式,源-监听模式或从属模式,是一种对象行为型模式。
(1)Subject(目标)目标又称为主题,是指被观察的对象,目标类中定义一个观察者集合,可以接受任意数量的观察者来观察,提供一系列方法来增加或者删除观察者对象,同时定义了通知方法nofity(),其可以是接口,可以是抽象类可以是具体类。
(2)ConcreteSubject(具体目标类)抽象目标的子类,通常包含经常发生改变的数据,当它的状态发生改变时,向各个观察者发出通知,同时也实现了抽象目标声明的公共方法,如果不需要扩展目标类,则可以去除。
(3)Observer(观察者)将对目标类的改变做出回应,一般定义为接口,该接口声明了更新数据的方法update,因此可以称之为抽象观察者。
(4)ConcreteObserver(具体观察者)维护一个指向具体目标对象的引用,它存储具体观察者的有关状态,这些状态需要和具体目标的状态保持一致,它实现了抽象观察者声明的update方法,通常在实现时,可用调用具体目标类的attach注入自己活着detach()方法将自己从目标类中删除。
如上所述,观察者模式包括观察目标和观察者两大类,一个目标可以有任意数目与之相依赖的观察者,一旦目标的状态发生改变,所有有关的观察者都将被通知。这种交互称之为发布-订阅模式。目标是发布者,它发生通知并不需要知道哪些是它的观察者
package com.learn.designmode.mode.observer;
import java.util.ArrayList;
import java.util.List;
/**
* 目标类
*/
public abstract class Subject {
// 定义一个观察者集合用于存储所有观察者对象
protected List<Observer> list = new ArrayList<>();
// 注册方法,向观察者集合添加一个观察者
private void attach(Observer observer){
list.add(observer);
}
// 注销方法
private void detach(Observer observer){
list.remove(observer);
}
// 声明抽象通知方法
public abstract void nofity();
}
class ConcreteSubject extends Subject{
/**
* 实现通知方法
*/
@Override
public void nofity() {
// 遍历观察者集合,调用每一个观察者的响应方法
for (Observer item:list){
item.update();
}
}
}
package com.learn.designmode.mode.observer;
/**
* 观察者
*/
public abstract class Observer {
public abstract void update();
}
class ConcreteObserver extends Observer{
@Override
public void update() {
// 具体业务代码
}
}
如果具体观察者要用到具体目标类里的状态的话:1、增加具体观察者符合开闭原则 2、增加具体目标类可能不符合。如果具体观察者没有用到具体目标类里的状态的话,符合开闭原则。
完整的解决方案
package com.learn.designmode.mode.observer.demo;
/**
* 抽象观察者类
*/
public interface Observer {
public String getName();
public void setName(String name);
// 声明应援方法
public void help();
// 声明被攻击方法
public void beAttached(AllyControlCenter controlCenter);
}
class Player implements Observer {
private String name;
@Override
public String getName() {
return this.name;
}
@Override
public void setName(String name) {
this.name = name;
}
/**
* 实现应援方法
*/
@Override
public void help() {
System.out.println("坚持住," + this.name +",来救你" );
}
/**
* 被攻击
*/
@Override
public void beAttached(AllyControlCenter allyControlCenter) {
allyControlCenter.nofity(this.name);
}
}
package com.learn.designmode.mode.observer.demo;
import java.util.ArrayList;
import java.util.List;
/**
* 战术控制中心(目标类)
*/
public abstract class AllyControlCenter {
protected String allyName;
protected List<Observer> list = new ArrayList<>();
/**
* 通知方法
*/
abstract void nofity(String name);
// 注册方法,向观察者集合添加一个观察者
public void attach(Observer observer){
list.add(observer);
}
// 注销方法
public void detach(Observer observer){
list.remove(observer);
}
}
class ConcreteAllyObserver extends AllyControlCenter{
public ConcreteAllyObserver(String allyName){
System.out.println("战队创建成功");
super.allyName = allyName;
}
@Override
void nofity(String name) {
System.out.println(name + "遭受攻击 ");
for(Observer item:this.list){
item.help();
}
}
}
package com.learn.designmode.mode.observer.demo;
public class Client {
public static void main(String[] args) {
AllyControlCenter allyControlCenter = new ConcreteAllyObserver("武林");
Observer observer1 = new Player();
observer1.setName("ZLX");
Observer observer2 = new Player();
observer2.setName("ZLX1");
Observer observer3 = new Player();
observer3.setName("ZLX2");
Observer observer4 = new Player();
observer4.setName("ZLX3");
allyControlCenter.attach(observer1);
allyControlCenter.attach(observer2);
allyControlCenter.attach(observer3);
allyControlCenter.attach(observer4);
observer1.beAttached(allyControlCenter);
}
}
JDK对观察者模式的支持
(1)Observer(抽象观察者)
当观察目标的状态发生改变时,该方法会被调用,在Obserer的子类中实现update方法,所以具体观察者有不同的更新行为。将调用观察者的nofity的方法时,将调用update方法
(2)Observerable类(观察目标)定义了一个Vector来存储观察者对象,所包含的方法和说明如下所示
观察者模式与Java事件处理
(1)点击一个按钮,触发一个ActionEvent类型的动作事件,JVM将产生一个相应的ActionEvent类型的事件对象,在该事件中包含了有关事件和事件源的信息,按钮是事件源对象。
(2)将ActionEvent事件对象传递给事件监听对象,JDK提供了专门处理ActionEvent事件的接口ActionListenter,开发人员需提供一个ActionListener的实现类,实现在ActionListenter接口中声明的抽象事件处理方法actionPerformed,对所发生的时间做出处理。
(3)开发人员将ActionListenter的实现类对象注册到按钮中,通过按钮类的addActionListenter来实现注册。
(4)JVM在触发事件将调用按钮的firexxxx方法,在该方法内部将调用注册到按钮事件处理对象的actionPerformed方法来实现注册。
(1)LoginEvent(事件类)用于封账与事件有关的信息,不是观察者模式的一部分,但是可以在目标对象和观察者对象之间传递数据,自定义事件类都是EventObject的子类。
(2)LoginEventListener(抽象观察者),声明了事件响应方法validateLogin,用于处理事件,方法将一个事件类对象作为参数,用于传输与事件相关的数据,在其子类实现该方法,实现具体的事件处理。
(3)LoginBean(目标类),定义了一个事件类对象le和抽象观察者对象lel,提供了注册方法addLoginEventListener用于添加观察者,但是这里是一对一的关系,并没有集合存储观察者对象,还定义了通知方法fireLoginEvent,该方法在java事件中称为点火方法,在该方法内部实例了一个事件对象LoginEvent,将用户输入的信息传给观察者对象,并且调用了观察者对象的响应方法validateLogin()
(4)LoginValidatetorX(具体观察类)实现了抽象观察者的方法用于具体事件的处理,可以针对不同的事件进行处理
观察者模式与MVC
总结
优点
(1)可以实现数据层与视图层的分离,定义了稳定的消息更新传递机制,并抽象了更新接口,使得可以有各种各样的表示层充当具体观察者角色
(2)观察者模式在观察目标和观察者之间建立一个抽象的耦合,观察目标只需要维持一个观察者的集合,并没有紧密的耦合在一起。
(3)支持广播通信,目标对象会向已经注入的观察者对象集合中发送通知,简化了一对多的设计难度。
(4)符合开闭原则,增加新的具体观察者无须修改源代码,在具体观察者与观察目标不存在关联关系的情况下,增加新的观察目标比较方便。
缺点
(1)如果一个观察对象有很多直接和间接观察者,将所有观察者都通知到会花很多时间(循环导致的)
(2)如果观察者和目标之间存在循环依赖,观察目标会触发他们之间进行循环调用,可能导致系统崩溃。
(3)观察者模式没有相应的机制让观察者知道所观察的目标是怎么发生变化的,而仅仅是知道了发生了变化
适用场景
(1)一个抽象模型有两个方面,其中一个方面依赖于另一个方面,将这两个方面封装在独立的对象中,使他们可以各自独立的改变和复用。
(2)一个对象的改变将导致一个或多个其他对象发生改变,而并不知道具体有多少对象将发生改变,也不知道这些对象是谁。
(3)需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象,可以使用观察者模式创建一种链式触发机制。