在处理类之间的多对一的依赖关系时,观察者设计模式应运而生了,它的出现实现了代码的瘦身,类之间的解耦,本文分三部分:
- 非观察者模式的多对一的依赖处理
- 观察者模式对多对一依赖处理的优化
- Java内置的观察者
假设场景: 前端用户向后端服务器发送不同的请求,后端的Selector区的分不同的请求,回调不同的Handler处理请求, 下面的示例代码
非观察者#
当Selector发现数据改变时,想要回调Handler的方法的前提是它要维护一个Handler的引用, 我们在构造函数中传递进去
@Data public class Selector { private String username; private String password; private Handler1 currentConditions; public Selector(Handler1 currentConditions) { this.currentConditions = new Handler1(); } public void datachange() { currentConditions.update(getUsername(),getPassword()); } // 添加假数据 public void setData(String username,String password){ this.username=username; this.password=password; datachange(); } }
Handkler 的成员变量和Selector一样, 同时真正干活的人是handler, 它里面只有干活的方法以及承载数据的字段, 干活的方法会被回调, 字段由Selector负责初始化
public class Handler1 { private String username; private String password; public void update( String username, String password){ this.username=username; this.password=password; display(); } public void display(){ System.out.println("Handler1 : username"+ username); System.out.println("Handler1 : password"+username); } }
测试:
public class text { public static void main(String[] args) { Handler1 currentConditions = new Handler1(); Selector Data = new Selector(currentConditions); Data.setData("张三","123"); } }
上面的设计方法实现了,当Selector发现新的数据改变时,回调指定的handler的需求,但是缺点很快就暴露出来, Handler引用以构造方法的形式在Selector维护,如果有100个handler, 是不是意味着Selector的构造函数重载100份?
观察者模式#
观察者解决了上面的问题,在观察者模式中有两个角色的划分,1.SubJect 2.Observer, 其中 SubJect负责回调Observer提供的方法处理数据, Observer就是观察者,伺机而动, 它之所以能够实现两者的解耦,实现代码的瘦身,精华就是在Subject的构造函数不再维护单一的某一个Observer的引用,而是一个泛型是为Observer类型的集合, 为了防止把非观察者的对象添加到集合中, 故 在原有的基础上,向上进行了一层抽象,把Subject和Observer设计成接口,Subject提供了注册/移除/通知观察者的方法,通知哪个方法呢? 于是Observer接口中提供了需要被回调的抽象方法, 做完这层封装之后,就有了这个观察者体系, 所有的观察者必须实现Observer接口,重写它的方法,任何回调观察者的类,也必须实现Subject,重写它的方法
实例代码:
public interface Subject { public void registerObserver(Observer o); public void removeObserver(Observer o); public void notifyObserver(); } public interface Observer { public void update(String m,String p); }
Subject的实现类:
@Data public class Selector implements Subject { private String username; private String password; private ArrayList<Observer> list; public Selector() { this.list = new ArrayList<Observer>(); } // 添加假数据 public void setData( String username, String password){ this.username=username; this.password=password; } @Override public void registerObserver(Observer o) { list.add(o); } @Override public void removeObserver(Observer o) { list.remove(o); } @Override public void notifyObserver() { list.forEach(s->s.update(getPassword(),getUsername())); } }
Observer的实现:
public class Handler1 implements Observer { private String username; private String password; @Override public void update( String username,String password){ this.username=username; this.password=password; display(); } public void display(){ System.out.println(" Handler1 "+ username); System.out.println(" Handler1 "+password); } }
测试:
public class text { public static void main(String[] args) { Handler1 secondConditions = new Handler1(); Selector Data = new Selector(); // 注册观察者 Data.registerObserver(secondConditions); Data.setData("张三","123"); Data.notifyObserver(); } }
我们自己实现的观察者函数回调的特性: : 虽然实现我们一开始的需求,但是可以看到,数据是在Subject
端进行初始化的, 并没有传递到观察端,由观察者自主分配数据
java内置的观察者#
java内置的观察中同样分为两种角色,1.Obserable 2.Observer 其中Observer是观察者,同样不变的是,他是个接口
但是: Obserable是一个抽象类, java为我们实现了 注册/移除,通知的方法, 在不允许多继承的java中,抽象类是有缺点的,这使得当前类不能再继承其他类
此外: 相对于我们自定义的观察者, 它做到了把数据传递到 观察者客户端,任由观察者自主的分配这些数据
如何使用:
对于Obserable来说, 继承他
- 由于java实现了主要的方法, 我们只关注如何使用这几个方法就行了
- 1: 在回调之前 , 必须先使用
this.setChanged();
否则信息不会通知到观察者 - 2: 通知的方法
this.notifyObservers(Object obj));
@Data public class Selector extends Observable { private String username; private String password; // 添加假数据 public void setData( String username, String password) { this.username = username; this.password = password; datachange(); } // 当信息更改, 就推送 public void datachange() { // todo 加上这一条,当有信息改变时, 就执行 this.setChanged(); // 通知时,把信息同步推送给观察者 this.notifyObservers(new Data(getUsername(),getPassword())); } @lombok.Data public class Data { private String username; private String password; public Data( String username, String password) { this.username = username; this.password = password; } } }
观察者: 继承Observer, 重写它被回调的方法
public class Handler1 implements Observer { private String username; private String password; @Override public void update(Observable observable, Object arg0) { this.password = ((Selector.Data) (arg0)).getPassword(); this.username = ((Selector.Data) (arg0)).getUsername(); display(); } public void display() { System.out.println("Handler1 : " + username); System.out.println("Handler1 : " + password); } }
测试
public class text { public static void main(String[] args) { Handler1 handler1 = new Handler1(); Selector weatherData = new Selector(); // 先注册的观察者,后执行 weatherData.addObserver(handler1); weatherData.setData("张三","123"); } }