设计模式03-观察者,策略和责任链
21中设计模式:
- 创建型:工厂方法模式 抽象工厂模式, 单例模式、 建造者模式 、 原型模式
- 结构模式: 代理模式、 适配器模式、装饰者模式、桥接模式、组合模式、享元模式、外观模式
- 行为模式:(11种) 模板模式、迭代子模式、命令模式、备忘录模式、状态模式、访问者模式、解释器模式、中介模式
🌼观察者模式(Observer Pattern)
定义
观察者模式 是软件设计模式的一种。 它定义了一种一对多
的依赖关系,让多个观察者对象同时监听某一个主题对象(被观察者)。这个主题对象在状态变化时,会通知所有的观察者对象,使他们能够自动更新自己。在观察者模式中,监视另一个对象状态的对象称为Observer,正在被监视的对象称为Subject。
现实例子
例如,某种商品的物价上涨时会导致部分商家高兴,而消费者伤心;还有,当我们开车到交叉路口时,遇到红灯会停,遇到绿灯会行。这样的例子还有很多,例如,股票价格与股民、微信公众号与微信用户、气象局的天气预报与听众、小偷与警察等。
角色
- 抽象主题(Subject)被观察者接口:
它把所有观察者对象的引用保存到一个集合里,
每个主题都可以有任何数量的观察者
。抽象主题提供一个接口,可以增加和删除观察者对象。
- 具体主题(Concrete Subject)被观察者接口的实现类:
将有关状态存入具体观察者对象;在具体主题内部状态改变时,
给所有登记过的观察者发出通知
。
- 抽象观察者(Observer):
为所有的具体观察者定义一个接口,在
得到主题通知时更新自己
。
- 具体观察者(Concrete Observer):
实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题状态协调。
实现
1. 观察者接口:
package com.aaa.observer;
/**
* @author : 尚腾飞(838449693@qq.com)
* @version : 1.0
* @createTime : 2022/9/27 14:55
* @description :
*/
public interface Observer {
void update(String message);
}
2. 观察者实现:
package com.aaa.observer;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author : 尚腾飞(838449693@qq.com)
* @version : 1.0
* @createTime : 2022/9/27 14:59
* @description :
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ConcreteObserver implements Observer {
private String obName;
@Override
public void update(String message) {
System.out.println("观察者:" + obName + "根据消息:" + message + "的变化,做出了具体的操作");
//执行具体操作
//。。。
}
}
3. 被观察者接口:
package com.aaa.observer;
/**
* @author : 尚腾飞(838449693@qq.com)
* @version : 1.0
* @createTime : 2022/9/27 15:02
* @description :
*/
public interface Subject {
/**
* 添加注册观察者
* @param observer
*/
void regObserver(Observer observer);
/**
* 删除观察者
* @param observer
*/
void delObserver(Observer observer);
/**
* 通知所有的观察者
* @param message
*/
void notifyObserver(String message);
}
4. 被观察者实现:
package com.aaa.observer;
import java.util.ArrayList;
import java.util.List;
/**
* @author : 尚腾飞(838449693@qq.com)
* @version : 1.0
* @createTime : 2022/9/27 15:04
* @description :被观察者的实现类
*/
public class SubjectImpl implements Subject {
//定义一个观察者(粉丝)列表
List<Observer> list = new ArrayList<>();
/**
* 添加观察者
* @param observer
*/
@Override
public void regObserver(Observer observer) {
list.add(observer);
}
/**
* 删除观察者
* @param observer
*/
@Override
public void delObserver(Observer observer) {
list.remove(observer);
}
/**
* 通知所有的观察者
* @param message
*/
@Override
public void notifyObserver(String message) {
if (list != null && list.size() > 0) {
for (Observer observer : list) {
observer.update(message);
}
}
}
}
5. 测试:
package com.aaa.observer;
/**
* 观察者模式测试
*/
public class ObserverTest {
public static void main(String[] args) {
//实例化处观者
Observer car = new ConcreteObserver("轿车");
Observer bus = new ConcreteObserver("公交");
Observer person = new ConcreteObserver("行人");
//实例化被观察者
Subject subject = new SubjectImpl();
//注册观察者
subject.regObserver(car);
subject.regObserver(bus);
subject.regObserver(person);
// 删除观察者
subject.delObserver(bus);
// 被观察者发生变化
subject.notifyObserver("红灯亮");
//结果
//...
}
}
总结
观察者模式将观察者和主题(被观察者)彻底解耦,主题只知道观察者实现了某一接口(也就是Observer接口)。并不需要观察者的具体类是谁、做了些什么或者其他任何细节。任何时候我们都可以增加新的观察者。因为主题唯一依赖的东西是一个实现了Observer接口的对象列表。
我们的web程序中使用到观察者模式的有web监听器。
🌼Spring的事件监听功能。
Spring的事件监听器就是利用的观察者模式
需求:注册事件,注册成功之后,添加初始积分,添加优惠券,发送短信
1. 定义一个spring的事件(被观察者)
package com.aaa.springListenner;
import org.springframework.context.ApplicationEvent;
/**
* @author : 尚腾飞(838449693@qq.com)
* @version : 1.0
* @createTime : 2022/9/27 16:08
* @description : 注册事件
*/
public class RegEvent extends ApplicationEvent {
private String name;
public RegEvent(Object source, String name) {
super(source);
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
2. 定义一个spring的事件监听器(观察者)
注意:监听器要注册为bean,方法上加@EventListener注解
package com.aaa.config;
import com.aaa.springListenner.RegEvent;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
/**
* @author : 尚腾飞(838449693@qq.com)
* @version : 1.0
* @createTime : 2022/9/27 16:11
* @description : spring的事件监听器(观察者)
*/
@Component
public class RegEventListener {
@EventListener
public void listenerReg(RegEvent regEvent) {
if ("注册成功".equals(regEvent.getName())) {
System.out.println("添加积分");
System.out.println("发送短信");
System.out.println("添加优惠券");
}
}
}
测试:
public static void main(String[] args) {
//获取ApplicationContext(spring容器)
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(RegEventListener.class);
RegEvent regEvent = new RegEvent(new Object(), "注册成功");
//context spring容器,发布事件
context.publishEvent(regEvent);
}
🌼策略模式(Strategy Pattern)
定义
定义了一组算法/业务方法,将每个算法/业务方法都封装起来,并且使它们之间可以互换。
现实例子
- 我们出门的时候会选择不同的出行方式,比如骑自行车、坐公交、坐火车、坐飞机、坐火箭等等,这些出行方式,每一种都是一个策略。
- 商场现在正在搞活动,有打折的、有满减的、有返利的等等,其实不管商场如何进行促销,说到底都是一些算法,这些算法本身只是一种策略,并且这些算法是随时都可能互相替换的,比如针对同一件商品,今天打八折、明天满100减30,这些策略间是可以互换的。
使用场景
- 如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
- 一个系统需要动态地在几种算法中选择一种。
- 如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。
- 不希望客户端知道复杂的、与算法相关的数据结构,在具体策略类中封装算法和相关的数据结构,提高算法的保密性与安全性。\
角色
策略模式包含如下角色:
- Context: 环境类
- Strategy: 抽象策略类
- ConcreteStrategy: 具体策略类
实现
需求:计算器的加减乘功能。
1. 抽象策略类
package com.aaa.strategy;
/**
* @author : 尚腾飞(838449693@qq.com)
* @version : 1.0
* @createTime : 2022/9/27 16:36
* @description :抽象策略接口
*/
public interface Strategy {
int calc(int a,int b);
}
2. 策略的实现类
加法策略
package com.aaa.strategy;
import com.aaa.strategy.Strategy;
/**
* @author : 尚腾飞(838449693@qq.com)
* @version : 1.0
* @createTime : 2022/9/27 16:37
* @description : 加法策略
*/
public class ConcreteStrategyAdd implements Strategy {
@Override
public int calc(int a, int b) {
return a+b;
}
}
减法策略
package com.aaa.strategy;
import com.aaa.strategy.Strategy;
/**
* @author : 尚腾飞(838449693@qq.com)
* @version : 1.0
* @createTime : 2022/9/27 16:37
* @description : 减法策略
*/
public class ConcreteStrategySub implements Strategy {
@Override
public int calc(int a, int b) {
return a-b;
}
}
乘法策略
package com.aaa.strategy;
import com.aaa.strategy.Strategy;
/**
* @author : 尚腾飞(838449693@qq.com)
* @version : 1.0
* @createTime : 2022/9/27 16:37
* @description : 乘法策略
*/
public class ConcreteStrategyMul implements Strategy {
@Override
public int calc(int a, int b) {
return a*b;
}
}
3. 环境类
package com.aaa.strategy;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author : 尚腾飞(838449693@qq.com)
* @version : 1.0
* @createTime : 2022/9/27 16:38
* @description :环境类
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Context {
//策略成员
Strategy strategy;
public int calc(int a,int b){
return strategy.calc(a,b);
}
}
4. 测试类
package com.aaa.strategy;
/**
* @author : 尚腾飞(838449693@qq.com)
* @version : 1.0
* @createTime : 2022/9/27 16:40
* @description :
*/
public class StrategyTest {
public static void main(String[] args) {
//初始化加法策略
ConcreteStrategyAdd strategyAdd = new ConcreteStrategyAdd();
//初始化乘法策略
ConcreteStrategyMul strategyMul = new ConcreteStrategyMul();
//初始化要使用那个策略
Context context = new Context(strategyAdd);
int calc = context.calc(12, 14);
System.out.println(calc);
}
}
总结
- 优点
- 策略模式提供了对“开闭原则”的完美支持,用户可以在不修改原有系统的基- 础上选 择算法或行为,也可以灵活地增加新的算法或行为。
- 策略模式提供了管理相关的算法族的办法。
- 使用策略模式可以避免使用多重条件转移语句。
- 缺点
- 策略类会增多。
- 所有策略类都需要对外暴露。(客户端必须知道所有的策略类,并自行决定使用哪一个策略类。)
🌼职责链(Chain of Responsibility Pattern)
定义
责任链模式是一种设计模式,在责任链模式里, 为了避免请求的发送者和接受者耦合在一起,于是将所有请求的处理者通过一个对象的引用记住其下家而连接起来形成一条链。并沿着这条链传递该请求,直到有一个对象处理它为止。
现实例子
请假流程(员工提交请假申请 班长审批(3天) 主管审批(7天) 主任审批(30天) 副总审批(180天))
采购流程(采购人填写采购单,采购主管审批,总监审批,总经理审批,财务审批,首付款,采购,验货,入库,付尾款)
报销流程(填写报销申请,粘贴附件,本部门负责人复核,财务复核,总经理审批,出纳付款)等等…
实现
1. 流程数据载体(请假单)
package com.aaa.chain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author :Teacher陈
* @date :Created in 2022/9/27 16:59
* @description:请假申请
* @modified By:
* @version: 1.0
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class LeaveApply {
/**
* 申请人
*/
String name;
/**
* 申请的天数
*/
int leaveDays;
}
2. 抽象责任处理类
package com.aaa.chain;
import lombok.AllArgsConstructor;
import lombok.Data;
/**
* @author :Teacher陈
* @date :Created in 2022/9/27 17:02
* @description:抽象的请假处理
* @modified By:
* @version:
*/
public abstract class AbstractLeaveHandler {
//1 班长审批(3天)
int level1 = 3;
// 2主管审批(7天)
int level2 = 7;
//3 主任审批(30天)
int level3 = 30;
// 4副总审批(180天)
int level4 = 180;
/**
* 处理人的姓名
*/
String name;
public AbstractLeaveHandler() {
}
public AbstractLeaveHandler(String name) {
this.name = name;
}
/**
* 下一级处理人员
*
* @param leaveApply
*/
AbstractLeaveHandler nextAbstractLeaveHandler;
abstract void handlerApply(LeaveApply leaveApply);
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public AbstractLeaveHandler getNextAbstractLeaveHandler() {
return nextAbstractLeaveHandler;
}
public void setNextAbstractLeaveHandler(AbstractLeaveHandler nextAbstractLeaveHandler) {
this.nextAbstractLeaveHandler = nextAbstractLeaveHandler;
}
}
3. 处理环节类
班长处理环节类
package com.aaa.chain;
/**
* @author :Teacher陈
* @date :Created in 2022/9/27 17:07
* @description:班长处理器
* @modified By:
* @version:
*/
public class MonitorHandler extends AbstractLeaveHandler {
@Override
void handlerApply(LeaveApply leaveApply) {
//处理请假申请
if (leaveApply.leaveDays <= super.level1) {
System.out.println("班长" + super.name + "审批了" + leaveApply.name + "的请假申请");
return;
}
//交个下一个处理器进行处理
if (null != this.nextAbstractLeaveHandler) {
this.nextAbstractLeaveHandler.handlerApply(leaveApply);
} else {
System.out.println("请假天数超过极限,直接离职!");
}
}
public MonitorHandler(String name) {
super(name);
}
}
主管处理环节类
package com.aaa.chain;
/**
* @author :Teacher陈
* @date :Created in 2022/9/27 17:07
* @description:主管处理器
* @modified By:
* @version:
*/
public class ManagerHandler extends AbstractLeaveHandler {
@Override
void handlerApply(LeaveApply leaveApply) {
//处理请假申请
if(leaveApply.leaveDays<=super.level2){
System.out.println("主管"+super.name+"审批了"+leaveApply.name+"的请假申请");
return;
}
//交个下一个处理器进行处理
if(null!=this.nextAbstractLeaveHandler){
this.nextAbstractLeaveHandler.handlerApply(leaveApply);
}else{
System.out.println("请假天数超过极限,直接离职!");
}
}
public ManagerHandler(String name) {
super(name);
}
}
主任处理环节类
package com.aaa.chain;
/**
* @author :Teacher陈
* @date :Created in 2022/9/27 17:07
* @description:主任处理器
* @modified By:
* @version:
*/
public class DirectorHandler extends AbstractLeaveHandler {
@Override
void handlerApply(LeaveApply leaveApply) {
//处理请假申请
if (leaveApply.leaveDays <= super.level3) {
System.out.println("主任" + super.name + "审批了" + leaveApply.name + "的请假申请");
return;
}
//交个下一个处理器进行处理
if (null != this.nextAbstractLeaveHandler) {
this.nextAbstractLeaveHandler.handlerApply(leaveApply);
} else {
System.out.println("请假天数超过极限,直接离职!");
}
}
public DirectorHandler(String name) {
super(name);
}
}
副总处理环节类
package com.aaa.chain;
/**
* @author :Teacher陈
* @date :Created in 2022/9/27 17:07
* @description:副总处理器
* @modified By:
* @version:
*/
public class VpHandler extends AbstractLeaveHandler {
@Override
void handlerApply(LeaveApply leaveApply) {
//处理请假申请
if (leaveApply.leaveDays <= super.level4) {
System.out.println("副总" + super.name + "审批了" + leaveApply.name + "的请假申请");
return;
}
//交个下一个处理器进行处理
if (null != this.nextAbstractLeaveHandler) {
this.nextAbstractLeaveHandler.handlerApply(leaveApply);
} else {
System.out.println("请假天数超过极限,直接离职!");
}
}
public VpHandler(String name) {
super(name);
}
}
4. 测试类
package com.aaa.chain;
/**
* @author :Teacher陈
* @date :Created in 2022/9/27 17:15
* @description:
* @modified By:
* @version:
*/
public class ChainTest {
public static void main(String[] args) {
//创建一个请假单
LeaveApply leaveApply = new LeaveApply("尚腾飞", 10);
//实例化责任链上的处理器
MonitorHandler monitorHandler = new MonitorHandler("刘振");
ManagerHandler managerHandler = new ManagerHandler("吴军燕");
DirectorHandler directorHandler = new DirectorHandler("吴军燕的领导");
VpHandler vpHandler = new VpHandler("陈建");
//设置下一步申请的流程
monitorHandler.setNextAbstractLeaveHandler(managerHandler);
managerHandler.setNextAbstractLeaveHandler(directorHandler);
directorHandler.setNextAbstractLeaveHandler(vpHandler);
//处理请假申请
monitorHandler.handlerApply(leaveApply);
}
}
总结
- 责任链主要重在责任分离处理,让各个节点各司其职。
- 责任链上的各个节点都有机会处理事务,但是也可能不会受理请求。
- 责任链比较长,调试时可能会比较麻烦。
- 责任链一般用于处理流程节点之类的实际业务场景中。(工作流)
Spring拦截器链、servlet过滤器链等都采用了责任链设计模式。