一 行为模式的概念
行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。
行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。
二 分类
行为型模式分为:
* 观察者模式
* 模板方法模式-类行为型模式
* 策略模式
* 职责链模式
* 状态模式
* 命令模式
* 中介者模式
* 迭代器模式
* 访问者模式
* 备忘录模式
* 解释器模式-类行为型模式
以上 11 种行为型模式,除了模板方法模式和解释器模式是类行为型模式,其他的全部属于对象行为型模式。
三 观察者模式
3.1 介绍
观察者模式(observer pattern)的原始定义是:定义对象之间的一对多依赖关系,这样当一个对象改变状态时,它的所有依赖项都会自动得到通知和更新。
3.2 原理
* Subject:抽象主题(抽象被观察者),抽象主题角色把所有观察者对象保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。
* ConcreteSubject:具体主题(具体被观察者),该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知。
* Observer:抽象观察者,是观察者的抽象类,它定义了一个更新接口,使得在得到主题更改通知时更新自己。
* ConcrereObserver:具体观察者,实现抽象观察者定义的更新接口,以便在得到主题更改通知时更新自身的状态。在具体观察者中维护一个指向具体目标对象的引用,它存储具体观察者的有关状态,这些状态需要与具体目标保持一致.
des: 解释一下上面的定义: 观察者模式它是用于建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应的作出反应.
在观察者模式中发生改变的对象称为**观察目标**,而被通知的对象称为**观察者**,一个观察目标可以应对多个观察者,而且这些观察者之间可以没有任何相互联系,可以根据需要增加和删除观察者,使得系统更易于扩展.
3.3 应用场景
> 观察者模式的别名有发布-订阅(Publish/Subscribe)模式,模型-视图(Model-View)模式、源-监听(Source-Listener) 模式等
3.3 结构图
3.4 实例1
3.4.1 观察者
/**
* 抽象观察者
**/
public interface Observer {
//update方法: 为不同的观察者更新行为定义一个相同的接口,不同的观察者对该接口有不同的实现
public void update();
}
/**
* 具体的观察者1
**/
public class ConcreteObserver1 implements Observer {
@Override
public void update() {
System.out.println("ConcreteObserver1 得到通知,更新状态! !");
}
}
/**具体观察者2
**/
public class ConcreteObserver2 implements Observer{
@Override
public void update() {
System.out.println("ConcreteObserver2 得到通知,更新状态! !");
}
}
3.4.2 观察目标 -被观察者
/**
* 抽象目标类
**/
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 observer : observers) {
observer.update();
}
}
}
3.4.3 调用
public class Client {
public static void main(String[] args) {
//创建目标类
Subject subject = new ConcreteSubject();
//注册观察者,注册多个
subject.attach(new ConcreteObserver1());
subject.attach(new ConcreteObserver2());
//具体的主题内部发生改变,给所有注册的观察者发送通知
subject.notifyObservers();
}
}
3.4 实例2
上面的摇号业务中,摇号、发短信、发MQ消息是一个顺序调用的过程,但是除了摇号这个核心功能以外, 发短信与记录信息到MQ的操作都不是主链路的功能,需要单独抽取出来,这样才能保证在后面的开发过程中保证代码的可扩展性和可维护性
3.4.1 观察者 事件监听(发送消息及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());
}
}
组合观察者的manager
/**
* 事件处理类
**/
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);
}
}
}
3.4.1 被观察者
/**
* 开奖服务接口
**/
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 lottery = lottery(uId);
//发送通知
eventManager.notify(EventManager.EventType.Message,lottery);
eventManager.notify(EventManager.EventType.MQ,lottery);
return lottery;
}
public abstract LotteryResult lottery(String uId);
}
/**
**/
public class LotteryServiceImpl extends LotteryService {
//注入摇号服务
private DrawHouseService houseService = new DrawHouseService();
@Override
public LotteryResult lottery(String uId) {
//1.摇号
String result = houseService.lots(uId);
return new LotteryResult(uId,result,new Date());
}
}
调用
LotteryService ls = new LotteryServiceImpl();
LotteryResult lotteryResult = ls.lotteryAndMsg("215673512673512736125763");
System.out.println(lotteryResult);
四 总结
### 6.1.5 观察者模式总结
**1) 观察者模式的优点**
* 降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。
* 被观察者发送通知,所有注册的观察者都会收到信息【可以实现广播机制】
**2) 观察者模式的缺点**
* 如果观察者非常多的话,那么所有的观察者收到被观察者发送的通知会耗时
* 如果被观察者有循环依赖的话,那么被观察者发送通知会使观察者循环调用,会导致系统崩溃
**3 ) 观察者模式常见的使用场景**
- 当一个对象状态的改变需要改变其他对象时。比如,商品库存数量发生变化时,需要通知商品详情页、购物车等系统改变数量。
- 一个对象发生改变时只想要发送通知,而不需要知道接收者是谁。比如,订阅微信公众号的文章,发送者通过公众号发送,订阅者并不知道哪些用户订阅了公众号。
- 需要创建一种链式触发机制时。比如,在系统中创建一个触发链,A 对象的行为将影响 B 对象,B 对象的行为将影响 C 对象……这样通过观察者模式能够很好地实现。
- 微博或微信朋友圈发送的场景。这是观察者模式的典型应用场景,一个人发微博或朋友圈,只要是关联的朋友都会收到通知;一旦取消关注,此人以后将不会收到相关通知。
- 需要建立基于事件触发的场景。比如,基于 Java UI 的编程,所有键盘和鼠标事件都由它的侦听器对象和指定函数处理。当用户单击鼠标时,订阅鼠标单击事件的函数将被调用,并将所有上下文数据作为方法参数传递给它。
**4 ) JDK 中对观察者模式的支持**
JDK中提供了Observable类以及Observer接口,它们构成了JDK对观察者模式的支持.
- `java.util.Observer` 接口: 该接口中声明了一个方法,它充当**抽象观察者**,其中声明了一个update方法.
```java
void update(Observable o, Object arg);
```
- `java.util.Observable` 类: 充当观察目标类(被观察类) , 在该类中定义了一个Vector集合来存储观察者对象.下面是它最重要的 3 个方法。
- void addObserver(Observer o) 方法:用于将新的观察者对象添加到集合中。
* void notifyObservers(Object arg) 方法:调用集合中的所有观察者对象的 update方法,通知它们数据发生改变。通常越晚加入集合的观察者越先得到通知。
* void setChange() 方法:用来设置一个 boolean 类型的内部标志,注明目标对象发生了变化。当它为true时,notifyObservers() 才会通知观察者。
用户可以直接使用Observer接口和Observable类作为观察者模式的抽象层,再自定义具体观察者类和具体观察目标类,使用JDK中提供的这两个类可以更加方便的实现观察者模式.