设计模式:11-状态模式 / 策略模式 / 职责链模式

22. 状态模式(State Pattern)

22.1 需求的引入

APP 抽奖活动问题 请编写程序完成 APP 抽奖活动 具体要求如下:

  1. 假如每参加一次这个活动要扣除用户 50 积分,中奖概率是10%
  2. 奖品数量固定,抽完就不能抽奖
  3. 活动有四个状态: 可以抽奖、不能抽奖、发放奖品和奖品领完
  4. 活动的四个状态转换关系图(如下图)

在这里插入图片描述

22.2 基本介绍

基本介绍

  1. 状态模式(State Pattern):它主要用来解决对象在多种状态转换时,需要对外输出不同的行为的问题。状态 和行为是一一对应的,状态之间可以相互转换
  2. 当一个对象的内在状态改变时,允许改变其行为,这个对象看起来像是改变了其类
  • 原理类图
    在这里插入图片描述

原理类图的说明-即(状态模式的角色及职责)

  1. Context 类为环境角色, 用于维护 State 实例,这个实例定义当前状态
  2. State 是抽象状态角色,定义一个接口封装与 Context 的一个特点接口相关行为
  3. 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 状态模式的注意事项和细节

  1. 代码有很强的可读性。状态模式将每个状态的行为封装到对应的一个类中
  2. 方便维护。将容易产生问题的 if-else 语句删除了,如果把每个状态的行为都放到一个类中,每次调用方法时都 要判断当前是什么状态,不但会产出很多 if-else 语句,而且容易出错
  3. 符合“开闭原则”。容易增删状态
  4. 会产生很多类。每个状态都要一个对应的类,当状态过多时会产生很多类,加大维护难度
  5. 应用场景:当一个事件或者对象有很多种状态,状态之间会相互转换,对不同的状态要求有不同的行为的时候, 可以考虑使用状态模式

23. 策略模式 (strategy pattern)

23.1 需求的引入

  1. 有各种鸭子(比如 野鸭、北京鸭、水鸭等, 鸭子有各种行为,比如 叫、飞行等)
  2. 显示鸭子的信息

23.2 基本介绍

基本介绍

  1. 策略模式(Strategy Pattern)中,定义算法族(策略组),分别封装起来,让他们之间可以互相替换,此模式 让算法的变化独立于使用算法的客户
  2. 这算法体现了几个设计原则,第一、把变化的代码从不变的代码中分离出来;第二、针对接口编程而不是具体 类(定义了策略接口);第三、多用组合/聚合,少用继承(客户通过组合方式使用策略)。
  • 原理类图
    在这里插入图片描述
  • 原理类图说明
  1. 从上图可以看到,客户 context 有成员变量 strategy 或者其他的策略接口 ,至于需要使用到哪个策略,我们可以在构造器中指定
  2. 策略模式:分别封装行为接口,实现算法族,超类里放行为接口对象,在子类里具体设定行为对象。原则就是: 分离变化部分,封装接口,基于接口编程各种功能。此模式让行为的变化独立于算法的使用者

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 策略模式的注意事项和细节

  1. 策略模式的关键是:分析项目中变化部分与不变部分
  2. 策略模式的核心思想是:多用组合/聚合 少用继承;用行为类组合,而不是行为的继承。更有弹性
  3. 体现了“对修改关闭,对扩展开放”原则,客户端增加行为不用修改原有代码,只要添加一种策略(或者行为) 即可,避免了使用多重转移语句(if…else if…else)
  4. 提供了可以替换继承关系的办法: 策略模式将算法封装在独立的 Strategy 类中使得你可以独立于其 Context 改 变它,使它易于切换、易于理解、易于扩展
  5. 需要注意的是:每添加一个策略就要增加一个类,当策略过多是会导致类数目庞

23. 职责链模式 (Chain of Responsibility Pattern)

23.1 需求的引入

采购员采购教学器材

  1. 如果金额 小于等于 5000, 由教学主任审批 (0<=x<=5000)
  2. 如果金额 小于等于 10000, 由院长审批 (5000<x<=10000)
  3. 如果金额 小于等于 30000, 由副校长审批 (10000<x<=30000)
  4. 如果金额 超过 30000 以上,有校长审批 ( 30000<x)

23.2 基本介绍

基本介绍

  1. 职责链模式(Chain of Responsibility Pattern), 又叫 责任链模式,为请求创建了一个接收者对象的链(简单示意 图)。这种模式对请求的发送者和接收者进行解耦。
  2. 职责链模式通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的 请求传给下一个接收者,依此类推。
  3. 这种类型的设计模式属于行为型模式
  • 原理类图
    在这里插入图片描述

  • 对原理类图的说明-即(职责链模式的角色及职责)

  1. Handler : 抽象的处理者, 定义了一个处理请求的接口, 同时含义另外 Handler
  2. ConcreteHandlerA , B 是具体的处理者, 处理它自己负责的请求, 可以访问它的后继者(即下一个处理者), 如果 可以处理当前请求,则处理,否则就将该请求交个 后继者去处理,从而形成一个职责链
  3. 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);
			}
		}
	}
}

源码总结

  1. springmvc 请求的流程图中,执行了 拦截器相关方法 interceptor.preHandler 等等
  2. 在处理 SpringMvc 请求时,使用到职责链模式还使用到适配器模式
  3. HandlerExecutionChain 主要负责的是请求拦截器的执行和请求处理,但是他本身不处理请求,只是将请求分配 给链上注册处理器执行,这是职责链实现方式,减少职责链本身与处理逻辑之间的耦合,规范了处理流程
  4. HandlerExecutionChain 维护了 HandlerInterceptor 的集合, 可以向其中注册相应的拦截器.

23.5 职责链模式的注意事项和细节

  1. 将请求和处理分开,实现解耦,提高系统的灵活性
  2. 简化了对象,使对象不需要知道链的结构
  3. 性能会受到影响,特别是在链比较长的时候,因此需控制链中最大节点数量,一般通过在 Handler 中设置一个 最大节点数量,在 setNext()方法中判断是否已经超过阀值,超过则不允许该链建立,避免出现超长链无意识地 破坏系统性能
  4. 调试不方便。采用了类似递归的方式,调试时逻辑可能比较复杂
  5. 最佳应用场景:有多个对象可以处理同一个请求时,比如:多级请求、请假/加薪等审批流程、Java Web 中 Tomcat 对 Encoding 的处理、拦截器
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值