zoukankan      html  css  js  c++  java
  • 设计模式(十三):状态模式

    一、概述

      状态模式允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它自己的类

    二、解决问题

      它主要用来解决对象在多种状态转换时,需要对外输出不同的行为的问题。状态和行为是一一对应的,状态之间可以相互转换。

    三、结构类图

    四、应用实例

      现在很多APP都有抽奖活动,我们在这里就用这个大家熟悉的例子来讲解状态模式。假如每参加一次这个活动要扣除用户50积分,中奖概率是10%,奖品数量固定,抽完就不能抽奖了。活动大概有下面四种状态,我们画出它的状态转化图。

    根据上图,我们梳理一下他们的关系,首先应该是定义出一个接口叫状态接口,每个状态都实现它。接口有扣除积分方法、抽奖方法、发放奖品方法,类图如下:

    有了这个类图,我们就可以开始编码了

    package com.jet.pattern.state;
    
    /**
     * description:
     * 状态接口
     * Created by Administrator on 2017/1/15.
     */
    public interface State {
        // 扣除积分
        public void deductMoney();
    
        // 是否抽中奖品
        public boolean raffle();
    
        // 发放奖品
        public void dispensePrize();
    
    }
    package com.jet.pattern.state.impl;
    
    import com.jet.pattern.state.State;
    
    /**
     * description:
     * 不能抽奖状态
     * Created by Administrator on 2017/1/15.
     */
    public class NoRaffleState implements State{
        // 初始化时传入活动引用,扣除积分后改变其状态
        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("不能发放奖品");
        }
    }
    package com.jet.pattern.state.impl;
    
    import com.jet.pattern.state.State;
    import sun.font.StandardTextSource;
    
    import java.util.Random;
    
    /**
     * description:
     * 可以抽奖状态
     * Created by Administrator on 2017/1/15.
     */
    public class CanRaffleState implements State {
        // 初始化时传入活动引用,抽奖后改变其状态
        RaffleActivity activity;
    
        public CanRaffleState(RaffleActivity activity) {
            this.activity = activity;
        }
    
        // 已经扣除了积分,不能再扣了
        @Override
        public void deductMoney() {
            System.out.println("已经扣取过了积分");
        }
    
        // 可以抽奖,抽完后改变其状态
        @Override
        public boolean raffle() {
            System.out.println("正在抽奖,请稍等!");
            Random r = new Random();
            int num = r.nextInt(10);
            // 10%中奖机会
            if(num == 0){
                // 改变活动状态为发放奖品
                activity.setState(activity.getDispenseState());
                return true;
            }else{
                System.out.println("很遗憾没有抽中奖品!");
                // 改变状态为不能抽奖
                activity.setState(activity.getNoRafflleState());
                return false;
            }
        }
    
        // 不能发放奖品
        @Override
        public void dispensePrize() {
            System.out.println("没中奖,不能发放奖品");
        }
    }
    package com.jet.pattern.state.impl;
    
    import com.jet.pattern.state.State;
    
    /**
     * description:
     * 发放奖品状态
     * Created by Administrator on 2017/1/15.
     */
    public class DispenseState implements State {
        // 初始化时传入活动引用,发放奖品后改变其状态
        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.getDispensOutState());
            }
    
        }
    }
    package com.jet.pattern.state.impl;
    
    import com.jet.pattern.state.State;
    
    /**
     * description:
     * 奖品发放完毕状态
     * Created by Administrator on 2017/1/15.
     */
    public class DispenseOutState implements State {
        // 初始化时传入活动引用
        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("奖品发送完了,请下次再参加");
        }
    }
    package com.jet.pattern.state.impl;
    
    import com.jet.pattern.state.State;
    
    /**
     * description:
     * 抽奖活动
     * Created by Administrator on 2017/1/15.
     */
    public class RaffleActivity {
        // 活动状态
        State state = null;
        // 奖品数量
        int count = 0;
        // 定义各种状态
        State noRafflleState = new NoRaffleState(this);
        State canRaffleState = new CanRaffleState(this);
        State dispenseState = new DispenseOutState(this);
        State dispensOutState = new DispenseOutState(this);
    
        public RaffleActivity( int count) {
            // 活动初始化为不能抽奖
            this.state = getNoRafflleState();
            this.count = count;
        }
    
        // 当用户想参加抽奖时,点击扣除积分
        public void debuctMoney(){
            state.deductMoney();
        }
    
        // 扣除积分后可以抽奖
        public void raffle(){
            // 是否抽中奖品
            if(state.raffle()){
                // 发放奖品是内部的处理逻辑,用户不可见
                state.dispensePrize();
            }
    
        }
    
        public State getState() {
            return state;
        }
    
        public void setState(State state) {
            this.state = state;
        }
    
        public int getCount() {
            return count;
        }
    
        public void setCount(int count) {
            this.count = count;
        }
    
        public State getNoRafflleState() {
            return noRafflleState;
        }
    
        public void setNoRafflleState(State noRafflleState) {
            this.noRafflleState = noRafflleState;
        }
    
        public State getCanRaffleState() {
            return canRaffleState;
        }
    
        public void setCanRaffleState(State canRaffleState) {
            this.canRaffleState = canRaffleState;
        }
    
        public State getDispenseState() {
            return dispenseState;
        }
    
        public void setDispenseState(State dispenseState) {
            this.dispenseState = dispenseState;
        }
    
        public State getDispensOutState() {
            return dispensOutState;
        }
    
        public void setDispensOutState(State dispensOutState) {
            this.dispensOutState = dispensOutState;
        }
    }
    package com.jet.pattern.state.test;
    
    import com.jet.pattern.state.impl.RaffleActivity;
    
    /**
     * description:
     * 状态模式测试类
     * Created by Administrator on 2017/1/15.
     */
    public class StateTest {
        public static void main(String[] args) {
            // 创建活动对象,奖品池有5个奖品
            RaffleActivity activity = new RaffleActivity(5);
    
            // 我们连续抽三次奖
            for (int i = 0; i < 3; i++) {
                System.out.println("--------第" + (i + 1) + "次抽奖----------");
                // 参加抽奖,第一步点击扣除积分
                activity.debuctMoney();
    
                // 第二步抽奖
                activity.raffle();
            }
        }
    }

    测试结果: 

    上面例子中,我们状态的改变是在状态类中改变的,其实也可以在活动类中改变,这取决于代码中那些部分是可以改动的。

    五、优缺点

      1、优点

      (1)、代码有很强的可读性。状态模式将每个状态的行为封装到对应的一个类中。

      (2)、方便维护。将容易产生问题的if-else语句删除了,如果把每个状态的行为都放到一个类中,每次调用方法时都要判断当前是什么状态,不但会产出很多if-else语句,而且容易出错。

      (3)、符合“开闭原则”。容易增删状态。

      2、缺点

      (1)、会产生很多类。每个状态都要一个对应的类,当状态过多时会产生很多类,加大维护难度。

    六、使用场景

      当一个事件或者对象有很多种状态,状态之间会相互转换,对不同的状态要求有不同的行为的时候使用。

    七、总结

      状态模式跟策略模式很相像,其结构类图基本一样,我们来做一下对比。

      (1)、行为是否固定。状态模式中,在不同的状态,其相同方法具有不同的行为。策略模式中,选择了一个策略后,其方法也是固定的。

      (2)、客户是否需要了解代码细节。状态模式中,客户不用管context对象的状态时如何运作的,只要调用其提供的方法就好了。策略模式中,客户调用context对象时,需要了解有哪些策略,最后选出一种策略。

      (3)、使用场景。状态模式的使用场景参看第六点。策略模式使用场景是作为继承的一种替代方案,客户端可以动态选择哪一种行为,而如果没有用策略,客户端每当需要一种新的行为时就要创建一个子类重写父类方法,这相当被动。

  • 相关阅读:
    Spring 中出现Element : property Bean definitions can have zero or more properties. Property elements correspond to JavaBean setter methods exposed by the bean classes. Spring supports primitives, refer
    java定时器schedule和scheduleAtFixedRate区别
    hql语句中的select字句和from 字句
    使用maven搭建hibernate的pom文件配置
    Failure to transfer org.apache.maven:maven-archiver:pom:2.5 from http://repo.maven.apache.org/ maven2 was cached in the local repository, resolution will not be reattempted until the update interv
    对于文件File类型中的目录分隔符
    hibernate的事务管理和session对象的详解
    解决mac 中的myeclipse控制台中文乱码问题
    ibatis selectKey用法问题
    Java中getResourceAsStream的用法
  • 原文地址:https://www.cnblogs.com/jenkinschan/p/6286472.html
Copyright © 2011-2022 走看看