简介

状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类当中, 可以把复杂的判断逻辑简化.

一个复杂的业务中可能存在大量的 if else 等逻辑条件判断, 对于后期维护来说是非常危险和复杂的。而状态模式也是将与特定状态相关的行为局部化, 并且将不同状态的行为分割开来.

可以消除大量的条件分支语句, 内部通过状态转移, 来减少之间的相互依赖

  • 思考: 策略模式、责任链模式和状态模式的区别是什么

场景设置

比如你在参与百度网盘开发, 有以下常见场景:

会员等级 权限
普通用户 存储照片、文件
会员 极速下载、5T 空间...
超级会员 小视频自动备份、音视频倍速播放...

简单实现

权限获取类

package design.pattern;

import java.util.ArrayList;

public class UserRule {

/**
 * 等级 1普通用户 2会员 3超级会员
 */
private Integer level = 1;

/**
 * 权限容器
 */
private ArrayList<String> ruleList = new ArrayList<String>() {
    {
        add(<span class="hljs-string">"上传文件"</span>);
        add(<span class="hljs-string">"下载文件"</span>);

    }
};

public UserRule(Integer level) {
    this.level = level;
}

/**
 * 获取权限列表
 *
 * @<span class="hljs-built_in">return</span>
 */
public ArrayList&lt;String&gt; <span class="hljs-function"><span class="hljs-title">getRuleList</span></span>() {

    <span class="hljs-keyword">if</span> (this.level == 2) {  //会员
        //todo 权限获取
        ruleList.add(<span class="hljs-string">"极速下载"</span>);
        ruleList.add(<span class="hljs-string">"5T空间"</span>);
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (this.level == 3) { //超级会员
        //todo 权限获取
        ruleList.add(<span class="hljs-string">"光速下载"</span>);
        ruleList.add(<span class="hljs-string">"10T空间"</span>);
        ruleList.add(<span class="hljs-string">"小视频自动备份"</span>);
        ruleList.add(<span class="hljs-string">"音视频倍速播放"</span>);
    }

    <span class="hljs-built_in">return</span> ruleList;
}

}

复制代码

客户端调用:

Integer requestLevel = 3;

UserRule userRule = new UserRule(requestLevel);

ArrayList<String> ruleList = userRule.getRuleList();

// 打印权限
System.out.println(“会员等级” + requestLevel + “权限列表:”);
for (Object object : ruleList) {
System.out.println(object);
}

复制代码

output:

会员等级3权限列表:
上传文件
下载文件
光速下载
10T空间
小视频自动备份
音视频倍速播放
复制代码

思考:

似乎看起来我们的代码足够简单, 很好的满足了根据等级返回权限的需求

我们重点关注下获取权限列表的根据不同的条件分支, 处理不同的 todo 的业务逻辑, 如果我们加入了更多的等级, 更复杂的权限计算方式等等功能, 这个 if else 将会更加的庞大起来.


刚刚说完可能庞大起来, 产品过来又给我提 v2.0 的需求.

  • 产品: 最近市场反馈说我们会员卖的不是很好, 现在我们要加一点点功能。
  • 我: ...
  • 产品: 就是再返回当前等级的时候, 把下个等级将要获取到的权限信息也给他返回, 让他看到, 鼓励用户做任务或付费.
  • 我: 我们不是有个列表可以让他直接对比看么, 为什么还要单独提示啊
  • 产品: 你是产品还是我是产品啊
  • 我: 心里默念《人人都是产品经理》

使用状态模式消息掉那些多余的 if else (当然还有那个最新的需求)

我们发现如果再加上返回下个版本的权限, 真的是够了, 再过几天不一定又出什么需求, 这个方法看起来都要崩溃了. 看来需要优化一下了。

首先我们建立一个抽象类 (核心作用方便子类约束和传递)

State.java

package design.pattern.Rules;

import design.pattern.UserRule;
import design.pattern.UserVo;

import java.util.ArrayList;

public abstract class State {

/**
 * 用户对象
 */
protected UserRule userRule;

/**
 * 权限容器
 */
protected ArrayList&lt;String&gt; ruleList = new ArrayList&lt;String&gt;() {
    {
        add(<span class="hljs-string">"上传文件"</span>);
        add(<span class="hljs-string">"下载文件"</span>);

    }
};

public State(UserRule userRule) {
    this.userRule = userRule;
}

public abstract ArrayList&lt;String&gt; getRuleList(UserVo userVo);

}

复制代码

第一步我们需要简单的参数对象 (这里用 view object)

UserVo.java

package design.pattern;

public class UserVo {

private String name;

private Integer level;

public UserVo(String name, Integer level) {
    this.name = name;
    this.level = level;
}

public String <span class="hljs-function"><span class="hljs-title">getName</span></span>() {
    <span class="hljs-built_in">return</span> name;
}

public Integer <span class="hljs-function"><span class="hljs-title">getLevel</span></span>() {
    <span class="hljs-built_in">return</span> level;
}

}

复制代码

继承 State.java 状态类, 实现各自的会员返回类

package design.pattern.Rules;

import design.pattern.UserRule;
import design.pattern.UserVo;

import java.util.ArrayList;

/**
* 一类会员
*/
public class MemberOne extends State {

public MemberOne(UserRule userRule) {
    super(userRule);
}

/**
 * 获取权限列表
 *
 * @<span class="hljs-built_in">return</span>
 */
public ArrayList&lt;String&gt; getRuleList(UserVo userVo) {

    //如果是一类会员(普通)
    <span class="hljs-keyword">if</span> (userVo.getLevel() == 1) {
        <span class="hljs-built_in">return</span> ruleList;
    } <span class="hljs-keyword">else</span> {
        userRule.setState(new MemberTwo(userRule)); //设置下一级别类
        <span class="hljs-built_in">return</span> userRule.getRuleList(userVo);  //获取下一个级别的详情
    }
}

}

复制代码

UserRule.java (桥梁类)

package design.pattern;

import design.pattern.Rules.MemberOne;
import design.pattern.Rules.State;

import java.util.ArrayList;

public class UserRule {

/**
 * 具体权限对象
 */
private State currentRule;

public <span class="hljs-function"><span class="hljs-title">UserRule</span></span>() {
    currentRule = new MemberOne(this);
}

/**
 * 设置权限对象
 *
 * @param state
 */
public void <span class="hljs-built_in">set</span>State(State state) {
    this.currentRule = state;
}


public ArrayList&lt;String&gt; getRuleList(UserVo userVo) {
    <span class="hljs-built_in">return</span> this.currentRule.getRuleList(userVo);
}

}

复制代码

userRule 类为我们优化前充满了条件判断的类, 对他进行了解耦合. 可以理解为对内调用类, 对外暴露类的桥梁类

userVo 对象是我们的参数对象, 这里主要用于等级判断. 如果不成立, 则进行重新设置下一个处理规则类, 并同样调用规则列表方法。

剩下的两个会员类

package design.pattern.Rules;

import design.pattern.UserRule;
import design.pattern.UserVo;

import java.util.ArrayList;

/**
* 三类会员
*/
public class MemberTwo extends State {

public MemberTwo(UserRule userRule) {
    super(userRule);
}

/**
 * 获取权限列表
 *
 * @<span class="hljs-built_in">return</span>
 */
public ArrayList&lt;String&gt; getRuleList(UserVo userVo) {

    <span class="hljs-keyword">if</span> (userVo.getLevel() == 2) {
        ruleList.add(<span class="hljs-string">"极速下载"</span>);
        ruleList.add(<span class="hljs-string">"5T空间"</span>);
        <span class="hljs-built_in">return</span> ruleList;
    }<span class="hljs-keyword">else</span>{
        userRule.setState(new MemberThree(userRule));
        <span class="hljs-built_in">return</span> userRule.getRuleList(userVo);
    }
}

}

复制代码
package design.pattern.Rules;

import design.pattern.UserRule;
import design.pattern.UserVo;

import java.util.ArrayList;

/**
* 三类会员
*/
public class MemberThree extends State {

public MemberThree(UserRule userRule) {
    super(userRule);
}

/**
 * 获取权限列表
 *
 * @<span class="hljs-built_in">return</span>
 */
public ArrayList&lt;String&gt; getRuleList(UserVo userVo) {

    //最高级
    ruleList.add(<span class="hljs-string">"光速下载"</span>);
    ruleList.add(<span class="hljs-string">"10T空间"</span>);
    ruleList.add(<span class="hljs-string">"小视频自动备份"</span>);
    ruleList.add(<span class="hljs-string">"音视频倍速播放"</span>);
    <span class="hljs-built_in">return</span> ruleList;
}

}

复制代码

为了参数方便传递管理, 我们单独使用一个 view object 类.

UserVo.java

package design.pattern;

public class UserVo {

private String name;

private Integer level;

public UserVo(String name, Integer level) {
    this.name = name;
    this.level = level;
}

public String <span class="hljs-function"><span class="hljs-title">getName</span></span>() {
    <span class="hljs-built_in">return</span> name;
}

public Integer <span class="hljs-function"><span class="hljs-title">getLevel</span></span>() {
    <span class="hljs-built_in">return</span> level;
}

public void <span class="hljs-built_in">set</span>Name(String name) {
    this.name = name;
}

public void <span class="hljs-built_in">set</span>Level(int level) {
    this.level = level;
}

}

复制代码

客户端调用:

UserVo userVo = new UserVo("小红", 1);

UserRule userRule = new UserRule();
ArrayList<String> ruleList = userRule.getRuleList(userVo);

// 打印
System.out.println(“用户” + userVo.getName() + “当前权限如下:”);
for (Object object : ruleList) {
System.out.println(object);
}

// 他的下个权限可以获得
userVo.setLevel(userVo.getLevel() + 1);
ruleList = userRule.getRuleList(userVo);
System.out.println(“用户” + userVo.getName() + “将要权限如下:”);
for (Object object : ruleList) {
System.out.println(object);
}

复制代码

output:

用户小红当前权限如下:
上传文件
下载文件
用户小红将要权限如下:
上传文件
下载文件
极速下载
5T空间
复制代码

核心代码:

if (userVo.getLevel() == 1) {
    return ruleList;
} else {
    userRule.setState(new MemberTwo(userRule)); //设置下一级别类
    return userRule.getRuleList(userVo);  //获取下一个级别的详情
}
复制代码

思考这段代码存在的问题? 如何优化?

状态模式 UML 图和策略模式一样

策略模式和状态模式区别在哪里?

策略模式与状态模式极其相似,但是二者有其内在的差别

  • 策略模式将具体策略类暴露出去,调用者需要具体明白每个策略的不同之处以便正确使用。
  • 状态模式状态的改变是由其内部条件来改变的, 与外界无关,二者在思想上有本质区别.
userRule.setState(new MemberTwo(userRule));),
复制代码

对比策略模式: 策略模式详解

更多精彩内容请关注热情小宇公众号 (呆呆熊一点通)

  • Java

    Java,是由 Sun Microsystems 公司于 1995 年 5 月推出的 Java 程序设计语言和 Java 平台的总称。用 Java 实现的 HotJava 浏览器(支持 Java applet)显示了 Java 的魅力:跨平台、动态的…

    380 引用 • 6 回帖
感谢    赞同    分享    收藏    关注    反对    举报    ...