文章目录
22. 状态模式(State Pattern)
22.1 需求的引入
APP 抽奖活动问题 请编写程序完成 APP 抽奖活动 具体要求如下:
- 假如每参加一次这个活动要扣除用户 50 积分,中奖概率是10%
- 奖品数量固定,抽完就不能抽奖
- 活动有四个状态: 可以抽奖、不能抽奖、发放奖品和奖品领完
- 活动的四个状态转换关系图(如下图)
22.2 基本介绍
基本介绍
- 状态模式(State Pattern):它主要用来解决对象在多种状态转换时,需要对外输出不同的行为的问题。状态 和行为是一一对应的,状态之间可以相互转换
- 当一个对象的内在状态改变时,允许改变其行为,这个对象看起来像是改变了其类
- 原理类图
原理类图的说明-即(状态模式的角色及职责)
- Context 类为环境角色, 用于维护 State 实例,这个实例定义当前状态
- State 是抽象状态角色,定义一个接口封装与 Context 的一个特点接口相关行为
- ConcreteState 具体的状态角色,每个子类实现一个与 Context 的一个状态相关行为
22.3 应用实例
- 思路分析和图解(类图)
- 源码
import java.util.concurrent.ThreadLocalRandom;
/**
* @author houyu
* @createTime 2020/2/11 19:52
*/
public class Demo {
public static void main(String[] args) {
RaffleActivity activity = new RaffleActivity(1);
// 我们连续抽 300 次奖
for(int i = 0; i < 30; i++) {
System.out.println("--------第" + (i + 1) + "次抽奖----------");
// 参加抽奖,第一步点击扣除积分
activity.deductMoney();
// 第二步抽奖
activity.raffle();
}
}
/**
* 抽象状态类
*/
public static abstract class State {
/** 扣除积分 - 50 */
public abstract void deductMoney();
/** 是否抽中奖品 */
public abstract boolean raffle();
/** 发放奖品 */
public abstract void dispensePrize();
}
/**
* 可以抽奖的状态
*/
public static class CanRaffleState extends State {
private RaffleActivity activity;
public CanRaffleState(RaffleActivity activity) {
this.activity = activity;
}
@Override
public void deductMoney() {
System.out.println("已经扣取过了积分");
}
@Override
public boolean raffle() {
int random = ThreadLocalRandom.current().nextInt(0, 10);
if(random == 0) {
System.out.println("恭喜你中奖了");
// 改变活动状态为发放奖品 context
activity.setState(activity.getDispenseState());
return true;
} else {
System.out.println("很遗憾没有抽中奖品!");
// 改变状态为不能抽奖
activity.setState(activity.getNoRaffleState());
return false;
}
}
@Override
public void dispensePrize() {
System.out.println("没中奖,不能发放奖品");
}
}
/**
* 奖品发放完毕状态
*/
public static class DispenseOutState extends State {
private RaffleActivity activity;
public DispenseOutState(RaffleActivity activity) {
this.activity = activity;
}
@Override
public void deductMoney() {
System.out.println("奖品发送完了,请下次再参加");
}
@Override
public boolean raffle() {
System.out.println("奖品发送完了,请下次再参加");
return false;
}
@Override
public void dispensePrize() {
System.out.println("奖品发送完了,请下次再参加");
}
}
/**
* 发放奖品的状态
*/
public static class DispenseState extends State {
private RaffleActivity activity;
public DispenseState(RaffleActivity activity) {
this.activity = activity;
}
@Override
public void deductMoney() {
System.out.println("不能扣除积分");
}
@Override
public boolean raffle() {
System.out.println("不能抽奖");
return false;
}
@Override
public void dispensePrize() {
if(activity.getCount() > 0) {
System.out.println("获得华为手机一台");
// 改变状态为不能抽奖 activity.setState(activity.getNoRafflleState());
} else {
System.out.println("很遗憾,奖品发送完了");
// 改变状态为奖品发送完毕, 后面我们就不可以抽奖
activity.setState(activity.getDispenseOutState());
System.out.println("抽奖活动结束");
}
}
}
/**
* 不能抽奖状态
*/
public static class NoRaffleState extends State {
private RaffleActivity activity;
public NoRaffleState(RaffleActivity activity) {
this.activity = activity;
}
@Override
public void deductMoney() {
System.out.println("扣除 50 积分成功,您可以抽奖了");
activity.setState(activity.getCanRaffleState());
}
@Override
public boolean raffle() {
System.out.println("扣了积分才能抽奖喔!");
return false;
}
@Override
public void dispensePrize() {
System.out.println("不能发放奖品");
}
}
public static class RaffleActivity {
/**
* state 表示活动当前的状态,是变化
*/
private State state;
/**
* 奖品数量
*/
private int count;
public RaffleActivity(int count) {
this.count = count;
state = noRaffleState;
}
State noRaffleState = new NoRaffleState(this);
State canRaffleState = new CanRaffleState(this);
State dispenseState = new DispenseState(this);
State dispenseOutState = new DispenseOutState(this);
public State getNoRaffleState() {
return noRaffleState;
}
public State getCanRaffleState() {
return canRaffleState;
}
public State getDispenseState() {
return dispenseState;
}
public State getDispenseOutState() {
return dispenseOutState;
}
public int getCount() {
return count--;
}
public void setState(State state) {
this.state = state;
}
/** 扣分, 调用当前状态的 deductMoney */
public void deductMoney() {
state.deductMoney();
}
/** 抽奖 */
public void raffle() {
// 如果当前的状态是抽奖成功
if(state.raffle()) {
// 领取奖品
state.dispensePrize();
}
}
}
}
22.4 状态模式的注意事项和细节
- 代码有很强的可读性。状态模式将每个状态的行为封装到对应的一个类中
- 方便维护。将容易产生问题的 if-else 语句删除了,如果把每个状态的行为都放到一个类中,每次调用方法时都 要判断当前是什么状态,不但会产出很多 if-else 语句,而且容易出错
- 符合“开闭原则”。容易增删状态
- 会产生很多类。每个状态都要一个对应的类,当状态过多时会产生很多类,加大维护难度
- 应用场景:当一个事件或者对象有很多种状态,状态之间会相互转换,对不同的状态要求有不同的行为的时候, 可以考虑使用状态模式
23. 策略模式 (strategy pattern)
23.1 需求的引入
- 有各种鸭子(比如 野鸭、北京鸭、水鸭等, 鸭子有各种行为,比如 叫、飞行等)
- 显示鸭子的信息
23.2 基本介绍
基本介绍
- 策略模式(Strategy Pattern)中,定义算法族(策略组),分别封装起来,让他们之间可以互相替换,此模式 让算法的变化独立于使用算法的客户
- 这算法体现了几个设计原则,第一、把变化的代码从不变的代码中分离出来;第二、针对接口编程而不是具体 类(定义了策略接口);第三、多用组合/聚合,少用继承(客户通过组合方式使用策略)。
- 原理类图
- 原理类图说明
- 从上图可以看到,客户 context 有成员变量 strategy 或者其他的策略接口 ,至于需要使用到哪个策略,我们可以在构造器中指定
- 策略模式:分别封装行为接口,实现算法族,超类里放行为接口对象,在子类里具体设定行为对象。原则就是: 分离变化部分,封装接口,基于接口编程各种功能。此模式让行为的变化独立于算法的使用者
23.3 应用实例
- 原理类图
- 代码实现
/**
* @author houyu
* @createTime 2020/2/11 20:45
*/
public class Demo {
public static void main(String[] args) {
ToyDuck toyDuck = new ToyDuck();
toyDuck.fly();
PekingDuck pekingDuck = new PekingDuck();
pekingDuck.fly();
pekingDuck.setFlyBehavior(new NoFlyBehavior());
pekingDuck.fly();
}
/**
* 飞翔接口
*/
public interface FlyBehavior {
/**
* 子类具体实现
*/
void fly();
}
/**
* 具体 飞翔接口 实现
*/
public static class GoodFlyBehavior implements FlyBehavior {
@Override
public void fly() {
System.out.println(" 飞翔技术高超 ~~~");
}
}
/**
* 具体 飞翔接口 实现
*/
public static class NoFlyBehavior implements FlyBehavior {
@Override
public void fly() {
System.out.println(" 不会飞翔 ~~~");
}
}
public interface QuackBehavior {
/**
* 子类实现
*/
void quack();
}
/**
* 抽象鸭
*/
public static abstract class Duck {
/**
* 属性, 策略接口
*/
FlyBehavior flyBehavior;
/**
* 其它属性<->策略接口
*/
// QuackBehavior quackBehavior;
public Duck() {}
public abstract void display();//显示鸭子信息
public void fly() {
//改进
if(flyBehavior != null) {
flyBehavior.fly();
}
}
public Duck setFlyBehavior(FlyBehavior flyBehavior) {
this.flyBehavior = flyBehavior;
return this;
}
}
/**
* 北京鸭
*/
public static class PekingDuck extends Duck {
public PekingDuck() {
flyBehavior = new GoodFlyBehavior();
}
@Override
public void display() {
System.out.println("~~北京鸭~~~");
}
}
/**
* 玩具鸭子
*/
public static class ToyDuck extends Duck{
public ToyDuck() {
flyBehavior = new NoFlyBehavior();
}
@Override
public void display() {
System.out.println("~~玩具鸭子~~~");
}
}
}
23.4 策略模式在 JDK-ArrayList 应用的源码分析
- ArrayList#sort 接受一个 Comparator 接口,具体的实现根据 传入的 Comparator 具体实现子类执行排序。
public void sort(Comparator<? super E> c) {
final int expectedModCount = modCount;
Arrays.sort((E[]) elementData, 0, size, c);
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
modCount++;
}
23.5 策略模式的注意事项和细节
- 策略模式的关键是:分析项目中变化部分与不变部分
- 策略模式的核心思想是:多用组合/聚合 少用继承;用行为类组合,而不是行为的继承。更有弹性
- 体现了“对修改关闭,对扩展开放”原则,客户端增加行为不用修改原有代码,只要添加一种策略(或者行为) 即可,避免了使用多重转移语句(if…else if…else)
- 提供了可以替换继承关系的办法: 策略模式将算法封装在独立的 Strategy 类中使得你可以独立于其 Context 改 变它,使它易于切换、易于理解、易于扩展
- 需要注意的是:每添加一个策略就要增加一个类,当策略过多是会导致类数目庞
23. 职责链模式 (Chain of Responsibility Pattern)
23.1 需求的引入
采购员采购教学器材
- 如果金额 小于等于 5000, 由教学主任审批 (0<=x<=5000)
- 如果金额 小于等于 10000, 由院长审批 (5000<x<=10000)
- 如果金额 小于等于 30000, 由副校长审批 (10000<x<=30000)
- 如果金额 超过 30000 以上,有校长审批 ( 30000<x)
23.2 基本介绍
基本介绍
- 职责链模式(Chain of Responsibility Pattern), 又叫 责任链模式,为请求创建了一个接收者对象的链(简单示意 图)。这种模式对请求的发送者和接收者进行解耦。
- 职责链模式通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的 请求传给下一个接收者,依此类推。
- 这种类型的设计模式属于行为型模式
-
原理类图
-
对原理类图的说明-即(职责链模式的角色及职责)
- Handler : 抽象的处理者, 定义了一个处理请求的接口, 同时含义另外 Handler
- ConcreteHandlerA , B 是具体的处理者, 处理它自己负责的请求, 可以访问它的后继者(即下一个处理者), 如果 可以处理当前请求,则处理,否则就将该请求交个 后继者去处理,从而形成一个职责链
- Request , 含义很多属性,表示一个请求
23.3 应用实例
- 思路类图
- 代码实现
/**
* @author houyu
* @createTime 2020/2/12 0:08
*/
public class Demo {
public static void main(String[] args) {
// 创建相关的审批人
DepartmentHandler departmentHandler = new DepartmentHandler("张主任");
CollegeHandler collegeHandler = new CollegeHandler("李院长");
ViceSchoolMasterHandler viceSchoolMasterHandler = new ViceSchoolMasterHandler("王副校");
SchoolMasterHandler schoolMasterHandler = new SchoolMasterHandler("佟校长");
// 需要将各个审批级别的下一个设置好(处理人构成环形)
departmentHandler.setHandler(collegeHandler);
collegeHandler.setHandler(viceSchoolMasterHandler);
viceSchoolMasterHandler.setHandler(schoolMasterHandler);
schoolMasterHandler.setHandler(departmentHandler);// 这里可以设置闭环,也可以不设置闭环,直接抛异常
// 创建请求
PurchaseRequest request = new PurchaseRequest("V1", 6000);
// 从下面调起
departmentHandler.processRequest(request);
// 从上面调起
schoolMasterHandler.processRequest(new PurchaseRequest("V1", 300));
/*
* 请求编号 id= V1 被 李院长 处理
* 请求编号 id= V1 被 张主任 处理
*/
}
public static class PurchaseRequest {
/** ID */
private String id;
/** 请求金额 */
private float price = 0.0f;
public PurchaseRequest(String id, float price) {
this.id = id;
this.price = price;
}
public String getId() {
return id;
}
public float getPrice() {
return price;
}
}
/**
* 处理基类
*/
public static abstract class Handler {
/** 下一个处理者, 构成环装 */
protected Handler handler;
/** 名字*/
protected String name;
public Handler(String name) {
this.name = name;
}
public Handler setHandler(Handler handler) {
this.handler = handler;
return this;
}
/**
* 处理审批请求的方法,得到一个请求, 处理是子类完成,因此该方法做成抽象
*/
public abstract void processRequest(PurchaseRequest purchaseRequest);
}
/**
* 校长
*/
public static class SchoolMasterHandler extends Handler {
public SchoolMasterHandler(String name) {
super(name);
}
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
if(purchaseRequest.getPrice() > 30000) {
System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理");
} else {
handler.processRequest(purchaseRequest);
}
}
}
/**
* 副校长
*/
public static class ViceSchoolMasterHandler extends Handler {
public ViceSchoolMasterHandler(String name) {
super(name);
}
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
if(purchaseRequest.getPrice() > 10000 && purchaseRequest.getPrice() <= 30000) {
System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理");
} else {
handler.processRequest(purchaseRequest);
}
}
}
/**
* 院长
*/
public static class CollegeHandler extends Handler {
public CollegeHandler(String name) {
super(name);
}
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
if(purchaseRequest.getPrice() > 5000 && purchaseRequest.getPrice() <= 10000) {
System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理");
} else {
handler.processRequest(purchaseRequest);
}
}
}
/**
* 主任
*/
public static class DepartmentHandler extends Handler {
public DepartmentHandler(String name) {
super(name);
}
@Override
public void processRequest(PurchaseRequest purchaseRequest) {
if(purchaseRequest.getPrice() <= 5000) {
System.out.println(" 请求编号 id= " + purchaseRequest.getId() + " 被 " + this.name + " 处理");
} else {
handler.processRequest(purchaseRequest);
}
}
}
}
23.4 职责链模式在 SpringMVC 框架应用的源码分析
- DispatcherServlet#doDispatch
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
源码总结
- springmvc 请求的流程图中,执行了 拦截器相关方法 interceptor.preHandler 等等
- 在处理 SpringMvc 请求时,使用到职责链模式还使用到适配器模式
- HandlerExecutionChain 主要负责的是请求拦截器的执行和请求处理,但是他本身不处理请求,只是将请求分配 给链上注册处理器执行,这是职责链实现方式,减少职责链本身与处理逻辑之间的耦合,规范了处理流程
- HandlerExecutionChain 维护了 HandlerInterceptor 的集合, 可以向其中注册相应的拦截器.
23.5 职责链模式的注意事项和细节
- 将请求和处理分开,实现解耦,提高系统的灵活性
- 简化了对象,使对象不需要知道链的结构
- 性能会受到影响,特别是在链比较长的时候,因此需控制链中最大节点数量,一般通过在 Handler 中设置一个 最大节点数量,在 setNext()方法中判断是否已经超过阀值,超过则不允许该链建立,避免出现超长链无意识地 破坏系统性能
- 调试不方便。采用了类似递归的方式,调试时逻辑可能比较复杂
- 最佳应用场景:有多个对象可以处理同一个请求时,比如:多级请求、请假/加薪等审批流程、Java Web 中 Tomcat 对 Encoding 的处理、拦截器