赞
踩
观察者模式的应用场景非常广泛,小到代码层面的解耦,大到架构层面的系统解耦,再或者 一些产品的设计思路,都有这种模式的影子.
现在我们常说的基于事件驱动的架构,其实也是观察者模式的一种最佳实践。当我们观察某一个对象时,对象传递出的每一个行为都被看成是一个事件,观察者通过处理每一个事件来完成自身的操作处理。
生活中也有许多观察者模式的应用,比如 汽车与红绿灯的关系,‘红灯停,绿灯行’,在这个过程中交通信号灯是汽车的观察目标,而汽车是观察者.
观察者模式(observer pattern)的原始定义是:定义对象之间的一对多依赖关系,这样当一个对象改变状态时,它的所有依赖项都会自动得到通知和更新。
解释一下上面的定义: 观察者模式它是用于建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应的作出反应.
在观察者模式中发生改变的对象称为观察目标,而被通知的对象称为观察者,一个观察目标可以应对多个观察者,而且这些观察者之间可以没有任何相互联系,可以根据需要增加和删除观察者,使得系统更易于扩展.
观察者模式的别名有发布-订阅(Publish/Subscribe)模式,模型-视图(Model-View)模式、源-监听(Source-Listener) 模式等
观察者模式结构中通常包括: 观察目标和观察者两个继承层次结构.
在观察者模式中有如下角色:
/** * 抽象观察者 **/ public interface Observer { //update方法: 为不同观察者的更新(响应)行为定义相同的接口,不同的观察者对该方法有不同的实现 public void update(); } /** * 具体观察者 * @author spikeCong * @date 2022/10/11 **/ public class ConcreteObserverOne implements Observer { @Override public void update() { //获取消息通知,执行业务代码 System.out.println("ConcreteObserverOne 得到通知!"); } } /** * 具体观察者 * @author spikeCong * @date 2022/10/11 **/ public class ConcreteObserverTwo implements Observer { @Override public void update() { //获取消息通知,执行业务代码 System.out.println("ConcreteObserverTwo 得到通知!"); } }
/** * 抽象目标类 **/ public interface Subject { void attach(Observer observer); void detach(Observer observer); void notifyObservers(); } /** * 具体目标类 **/ public class ConcreteSubject implements Subject { //定义集合,存储所有观察者对象 private ArrayList<Observer> observers = new ArrayList<>(); //注册方法,向观察者集合中增加一个观察者 @Override public void attach(Observer observer) { observers.add(observer); } //注销方法,用于从观察者集合中删除一个观察者 @Override public void detach(Observer observer) { observers.remove(observer); } //通知方法 @Override public void notifyObservers() { //遍历观察者集合,调用每一个观察者的响应方法 for (Observer obs : observers) { obs.update(); } } }
public class Client {
public static void main(String[] args) {
//创建目标类(被观察者)
ConcreteSubject subject = new ConcreteSubject();
//注册观察者类,可以注册多个
subject.attach(new ConcreteObserverOne());
subject.attach(new ConcreteObserverTwo());
//具体主题的内部状态发生改变时,给所有注册过的观察者发送通知。
subject.notifyObservers();
}
}
接下来我们使用观察模式,来实现一个买房摇号的程序.摇号结束,需要通过短信告知用户摇号结果,还需要想MQ中保存用户本次摇号的信息.
未使用设计模式
/** * 模拟买房摇号服务 **/ public class DrawHouseService { //摇号抽签 public String lots(String uId){ if(uId.hashCode() % 2 == 0){ return "恭喜ID为: " + uId + " 的用户,在本次摇号中中签! !"; }else{ return "很遗憾,ID为: " + uId + "的用户,您本次未中签! !"; } } } public class LotteryResult { private String uId; // 用户id private String msg; // 摇号信息 private Date dataTime; // 业务时间 //get&set..... } /** * 开奖服务接口 **/ public interface LotteryService { //摇号相关业务 public LotteryResult lottery(String uId); } /** * 开奖服务 **/ public class LotteryServiceImpl implements LotteryService { //注入摇号服务 private DrawHouseService houseService = new DrawHouseService(); @Override public LotteryResult lottery(String uId) { //摇号 String result = houseService.lots(uId); //发短信 System.out.println("发送短信通知用户ID为: " + uId + ",您的摇号结果如下: " + result); //发送MQ消息 System.out.println("记录用户摇号结果(MQ), 用户ID:" + uId + ",摇号结果:" + result); return new LotteryResult(uId,result,new Date()); } } @Test public void test1(){ LotteryService ls = new LotteryServiceImpl(); String result = ls.lottery("1234567887654322"); System.out.println(result); }
使用观察者模式进行优化
上面的摇号业务中,摇号、发短信、发MQ消息是一个顺序调用的过程,但是除了摇号这个核心功能以外, 发短信与记录信息到MQ的操作都不是主链路的功能,需要单独抽取出来,这样才能保证在后面的开发过程中保证代码的可扩展性和可维护性.
/** * 事件监听接口 **/ public interface EventListener { void doEvent(LotteryResult result); } /** * 短信发送事件 **/ public class MessageEventListener implements EventListener { @Override public void doEvent(LotteryResult result) { System.out.println("发送短信通知用户ID为: " + result.getuId() + ",您的摇号结果如下: " + result.getMsg()); } } /** * MQ消息发送事件 **/ public class MQEventListener implements EventListener { @Override public void doEvent(LotteryResult result) { System.out.println("记录用户摇号结果(MQ), 用户ID:" + result.getuId() + ",摇号结果:" + result.getMsg()); } }
事件处理
/** * 事件处理类 **/ public class EventManager { public enum EventType{ MQ,Message } //监听器集合 Map<Enum<EventType>, List<EventListener>> listeners = new HashMap<>(); public EventManager(Enum<EventType>... operations) { for (Enum<EventType> operation : operations) { this.listeners.put(operation,new ArrayList<>()); } } /** * 订阅 * @param eventType 事件类型 * @param listener 监听 */ public void subscribe(Enum<EventType> eventType, EventListener listener){ List<EventListener> users = listeners.get(eventType); users.add(listener); } /** * 取消订阅 * @param eventType 事件类型 * @param listener 监听 */ public void unsubscribe(Enum<EventType> eventType,EventListener listener){ List<EventListener> users = listeners.get(eventType); users.remove(listener); } /** * 通知 * @param eventType 事件类型 * @param result 结果 */ public void notify(Enum<EventType> eventType, LotteryResult result){ List<EventListener> users = listeners.get(eventType); for (EventListener listener : users) { listener.doEvent(result); } } }
摇号业务处理
/** * 开奖服务接口 **/ public abstract class LotteryService{ private EventManager eventManager; public LotteryService(){ //设置事件类型 eventManager = new EventManager(EventManager.EventType.MQ, EventManager.EventType.Message); //订阅 eventManager.subscribe(EventManager.EventType.Message,new MessageEventListener()); eventManager.subscribe(EventManager.EventType.MQ,new MQEventListener()); } public LotteryResult lotteryAndMsg(String uId){ LotteryResult result = lottery(uId); //发送通知 eventManager.notify(EventManager.EventType.Message,result); eventManager.notify(EventManager.EventType.MQ,result); return result; } public abstract LotteryResult lottery(String uId); } /** * 开奖服务 **/ public class LotteryServiceImpl extends LotteryService { //注入摇号服务 private DrawHouseService houseService = new DrawHouseService(); @Override public LotteryResult lottery(String uId) { //摇号 String result = houseService.lots(uId); return new LotteryResult(uId,result,new Date()); } }
测试
@Test
public void test2(){
LotteryService ls = new LotteryServiceImpl();
LotteryResult result = ls.lotteryAndMsg("1234567887654322");
System.out.println(result);
}
3 ) 观察者模式常见的使用场景
JDK中提供了Observable类以及Observer接口,它们构成了JDK对观察者模式的支持.
java.util.Observer
接口: 该接口中声明了一个方法,它充当抽象观察者,其中声明了一个update方法.
void update(Observable o, Object arg);
java.util.Observable
类: 充当观察目标类(被观察类) , 在该类中定义了一个Vector集合来存储观察者对象.下面是它最重要的 3 个方法。
void notifyObservers(Object arg) 方法:调用集合中的所有观察者对象的 update方法,通知它们数据发生改变。通常越晚加入集合的观察者越先得到通知。
void setChange() 方法:用来设置一个 boolean 类型的内部标志,注明目标对象发生了变化。当它为true时,notifyObservers() 才会通知观察者。
用户可以直接使用Observer接口和Observable类作为观察者模式的抽象层,再自定义具体观察者类和具体观察目标类,使用JDK中提供的这两个类可以更加方便的实现观察者模式.
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。