观察者模式
@author 无忧少年
@createTime 2019/08/20
观察者模式:定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主体对象在状态发生变化时,会通知所有观察者对象,使他们能够自动更新自己。观察者模式又叫发布-订阅模式
观察者模式(Observer)结构图
Subject类,可翻译为主体或冲向通知者,一般用一个抽象类或者一个接口实现,它把所有对观察者对象的引用保存在一个集合里,每个主体都可以由任何数量的观察者。抽象主体提供一个接口,可以增加和删除观察者对象。
/**
* @description: 主体/抽象通知者
* @author: lisiwen
* @create: 2019-08-21 08:46
**/
abstract class Subject {
private List<Observer> observerList=new ArrayList<Observer>();
public void Attach(Observer observer){
this.observerList.add(observer);
}
public void Detach(Observer observer){
this.observerList.remove(observer);
}
public void Notify(){
for(Observer observer : observerList ){
observer.Update();
}
}
}
Observer类,抽象观察者,为所有的具体观察者定义一个接口,在得到主题通知时更新自己。这个接口叫做更新接口。抽象观察者一般用一个抽象类或者一个接口实现,更新接口通常包含一个Update()方法,这个方法叫做更新方法。
/**
* @description: 抽象观察者
* @author: lisiwen
* @create: 2019-08-21 08:48
**/
abstract class Observer {
public abstract void Update();
}
ConcreteSubject类,叫做具体主体或具体通知者,将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色通常用一个具体子类实现。
/**
* @description: 具体主题/具体通知者
* @author: lisiwen
* @create: 2019-08-21 08:48
**/
public class ConcreteSubject extends Subject{
private String subjectState;
public String getSubjectState() {
return subjectState;
}
public void setSubjectState(String subjectState) {
this.subjectState = subjectState;
}
}
ConcreteObserver类,具体观察者,实现抽象观察者角色所要求的更新接口,以便使本身的状态与主体的状态相协调。具体观察者角色可以保存一个只想主题对象的引用。具体观察者角色通常使用一个具体子类实现。
/**
* @description: 具体观察者
* @author: lisiwen
* @create: 2019-08-21 08:49
**/
public class ConcreteObserver extends Observer {
private String name;
private String observerState;
private ConcreteSubject concreteSubject;
public ConcreteObserver(ConcreteSubject concreteSubject, String name) {
this.concreteSubject = concreteSubject;
this.name = name;
}
@Override
public void Update() {
observerState = concreteSubject.getSubjectState();
System.out.println("观察者" + this.name + "的新状态是" + this.observerState);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getObserverState() {
return observerState;
}
public void setObserverState(String observerState) {
this.observerState = observerState;
}
public ConcreteSubject getConcreteSubject() {
return concreteSubject;
}
public void setConcreteSubject(ConcreteSubject concreteSubject) {
this.concreteSubject = concreteSubject;
}
}
客户端代码
/**
* @description: 客户端
* @author: lisiwen
* @create: 2019-08-21 08:49
**/
public class Main {
public static void main(String[] args) {
ConcreteSubject subject=new ConcreteSubject();
subject.Attach(new ConcreteObserver(subject,"X"));
subject.Attach(new ConcreteObserver(subject,"Y"));
subject.Attach(new ConcreteObserver(subject,"Z"));
subject.setSubjectState("ABC");
subject.Notify();
}
}
/// 控制台输出
观察者X的新状态是ABC
观察者Y的新状态是ABC
观察者Z的新状态是ABC
**观察者模式的动机:**将一个系统分割成一系列相互协作的类有一个很不好的副作用没那就是需要维护相关对象间的一致性。我们不希望为了维持一致性而使各类紧密聚合,这样会给维护、扩展和重用都带来不便。
**观察者模式使用场景:**当一个对象的改变需要同时改变其他对象,而且他不知道具体有多少对象有待改变时,应该考虑使用观察者模式。
观察者模式所作的工作其实就是在接触耦合。让耦合的双方都依赖于抽象而不是依赖于具体。从而使得各自的变化都不会影响另一边的变化。
观察者模式中的观察者和通知者还是耦合的,如果两个观察者在做不同的事情也就是方法名不一样,通知者可能就只能通知其中的一个人,解决的办法是使用事件委托
,使用java中反射的机制,将观察者和通知者完全脱耦。具体代码如下:
/**
* @description: 事件对象的封装类
* @author: lisiwen
* @create: 2019-08-22 09:56
**/
public class Event {
//要执行方法的对象
private Object object;
//要执行的方法名称
private String methodName;
//要执行方法的参数
private Object[] params;
//要执行方法的参数类型
private Class[] paramTypes;
public Event() {
}
public Event(Object object, String methodName, Object... args) {
this.object = object;
this.methodName = methodName;
this.params = args;
contractParamTypes(this.params);
}
//根据参数数组生成参数类型数组
private void contractParamTypes(Object[] params) {
this.paramTypes = new Class[params.length];
for (int i = 0; i < params.length; i++) {
this.paramTypes[i] = params[i].getClass();
}
}
public Object getObject() {
return object;
}
//这里省略了若干get和set方法
/**
* 根据该对象的方法名,方法参数,利用反射机制,执行该方法
*
* @throws Exception
*/
public void invoke() throws Exception {
Method method = object.getClass().getMethod(this.getMethodName(),
this.getParamTypes());
if (null == method) {
return;
}
method.invoke(this.getObject(), this.getParams());
}
public void setObject(Object object) {
this.object = object;
}
public String getMethodName() {
return methodName;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
public Object[] getParams() {
return params;
}
public void setParams(Object[] params) {
this.params = params;
}
public Class[] getParamTypes() {
return paramTypes;
}
public void setParamTypes(Class[] paramTypes) {
this.paramTypes = paramTypes;
}
}
/**
* @description: 事件的 处理者
* @author: lisiwen
* @create: 2019-08-22 09:57
**/
public class EventHandler {
//是用一个List
private List<Event> objects;
public EventHandler(){
objects=new ArrayList<Event>();
}
//添加某个对象要执行的事件,及需要的参数
public void addEvent(Object object,String methodName,Object...args){
objects.add(new Event(object,methodName,args));
}
//通知所有的对象执行指定的事件
public void notifyX() throws Exception{
for(Event e : objects){
e.invoke();
}
}
}
Subject类,可翻译为主体或冲向通知者,一般用一个抽象类或者一个接口实现,它把所有对观察者对象的引用保存在一个集合里,每个主体都可以由任何数量的观察者。抽象主体提供一个接口,可以增加和删除观察者对象。
/**
* @description: 主体/抽象通知者
* @author: lisiwen
* @create: 2019-08-22 09:57
**/
abstract class Subject {
private EventHandler eventHandler = new EventHandler();
public EventHandler getEventHandler() {
return eventHandler;
}
public void setEventHandler(EventHandler eventHandler) {
this.eventHandler = eventHandler;
}
/**
* @param object 要执行方法的对象
* @param methodName 执行方法 的方法名
* @param args 执行方法的参数
*/
public abstract void addListener(Object object, String methodName,
Object... args);
/**
* 告诉所有要帮忙通知的观察者
*/
public abstract void notifyX();
}
ConcreteSubject类,叫做具体主体或具体通知者,将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。具体主题角色通常用一个具体子类实现。
/**
* @description: 具体主题/具体通知者
* @author: lisiwen
* @create: 2019-08-22 09:58
**/
public class ConcreteSubject extends Subject{
@Override
public void addListener(Object object, String methodName, Object... args) {
EventHandler handler = this.getEventHandler();
handler.addEvent(object, methodName, args);
}
@Override
public void notifyX() {
try{
this.getEventHandler().notifyX();
}catch(Exception e){
e.printStackTrace();
}
}
}
看股票的观察者
/**
* @description: 看股票的观察者
* @author: lisiwen
* @create: 2019-08-22 09:58
**/
public class NbaWatcher {
protected String name; //该观察者的名字
public NbaWatcher(String name) {
this.name = name;
}
public void CloseNbaWatcher(String action) {
// TODO Auto-generated method stub
System.out.println(action + name + "别看球赛啦,继续学习");
}
}
看世界杯的观察者
/**
* @description: 看世界杯的观察者
* @author: lisiwen
* @create: 2019-08-22 09:59
**/
public class ComicReader {
protected String name; //该观察者的名字
public ComicReader(String name) {
this.name = name;
}
public void CloseComicReader(String action) {
// TODO Auto-generated method stub
System.out.println(action + name + "别看漫画啦,继续学习");
}
}
客户端代码
/**
* @description: 客户端
* @author: lisiwen
* @create: 2019-08-22 09:59
**/
public class Main {
public static void main(String[] args) {
//创建一个尽职尽责的通知者
Subject goodNotifier = new ConcreteSubject();
//创建一个玩游戏的观察者,开始玩游戏
ComicReader comicReader = new ComicReader("ComicReader");
//创建一个看电视的观察者,开始看电视
NbaWatcher nbaWatcher = new NbaWatcher("NbaWatcher");
//玩游戏的观察者告诉通知者,有情况告诉一下
goodNotifier.addListener(comicReader, "CloseComicReader", "有人来了!");
//看电视的观察者告诉通知者,有情况告诉一下
goodNotifier.addListener(nbaWatcher, "CloseNbaWatcher", "有人来了!");
try {
//一点时间后
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
//有情况出现,通知者的人通知所有要帮忙的观察者
goodNotifier.notifyX();
}
}
/// 控制台输出
有人来了!ComicReader别看漫画啦,继续学习
有人来了!NbaWatcher别看球赛啦,继续学习