【一起学系列】之状态模式:你听过“流程”模式吗?

目的

容许一个方针在其内部情况改动时改动它的行为

说人话:容许方针在改动本身情况时分,更改绑定的特定方法

情况方式的诞生

产品】:Hello,开发小哥,我们需求开发一款 娃娃机 ,你可以提前想想怎样规划它啦。

开发】:娃娃机?我想想奥,它需求投币,用户移动,供认抓取,结束这几R F G i |个动作,好像很好做欸,用一个变量维护它其时的阶段,然后写四个 if 句子就好啦。

BOSS】:你准备用一个主方法,四个子方法Y e 4合作 if 句子外加一个情况变量去做吗?

// 伪b M { C ( B !代码
public void handle() {
    if (flag == A) {
        a();
    }

    if (flag =` U Y ) 9 - A U K= B) {
        b();
    }
}

开发】:对啊,老迈,你真是我肚子里的蛔虫!

BOSS】:蛔你个头,这样做 大错特错!,你莫m Z p { & B 0非想对 投币口,按钮,摇杆都绑定同一个方法吗?

开发】:对哦,它们应该是 不同的方法,一同露出给用户,我再考虑考虑

【一同学系列】之情况方式:你听过“流程”方式吗?

HeadFirst 中心代码

定义情况接口,一同封装改动,运用default要害@ H D字封装默许方法

public interfacez W 3 = ( State {

    /** 投币 **/
    default vox d 8 p S s Jid giveMoney() {
        System.out.println("无法投币");
    }

    /** 移动滑杆 **/
    default vI S : # T A 8 Y boid mo? S E h K c N = 3ve() {
        System.out.println("无法移动滑杆");
    }

    /** 抓取 **/
    default void grab() {
        System.out.println("无法抓取");
    }

    void changeStatB l K Te();
}

投币情况 情况的其间之一

public class MoneyState implements Stat= ? @ V H C * `e{

    Context context;

    public MoneyState(Context context) {
        this.context = context;
    }

    @Override
    public void giveMoney() {
        System.out.println("已投币!");
        changeState();
    }

    @Override
    public void chaU ^ L | [ } m c ^ngeState() {
        context.setExecute(new MoveState(context));
    }
}

为了尽量减少代码,只展现了其间一种情况,我们可以看到在 MoneyState 情况类实行所属的业务方法时,更改了上下文持有的情况类,[ E 5 ( % ~ U U R这就产生了 情况的改动 ,一同上下文更加明晰,即:我只用考虑我下z u k V f % ) i n一个情况是什么

情况方式的规划思路:

  • Context 上下文环境,持有情况
  • State 情况顶层接口
  • ConcreteStatG v ? 8 q 6e 具体的情况

简略来说,

  1. 有必要明晰的认识到共有多少种不同的情况,并经过接口定义其间心方法,封装改动
  2. 情况类持有 Context 上下文,在中心方法处理后更改其情况

假设看g x L S f e b ! )着有点模棱两可0 S % A,主张看完本文后,访问专题7 i v b R ? ? S S规划方式开源项目,里面有具体的代码示例,链接在最下面

情况方式的要害

  • 明晰一切或许发生的情况,及其转化联络
  • 明晰情况方式中的各个情况是有或许一同露出给用户的

就好像娃b S S K Z 7娃机运作的多种情况, 投币,移I : ]动摇杆,按下供认按钮等等或许不按先后顺序触发

整一个 “流程” 方式

每个情况的方法名都相同会怎样Z n l + n Y b

上文中我们大概知道了情况方式的特色,把情况封装成类,在调用情况-中心方法时分更改其情况本身,此时考虑的多种情况方法名或许各不相同,假设我们都起相同的姓名会怎样?

我们会首要遇到一个问题,我们无法得知它需求调用几回方法(因为或许有重复性 A – B 的情况),但假设无限循环,在恰当的当地控制其结束点,和是否持续实行的标识,好像就可以解决了。@ J j

来一个流程案例

【一同学系列】之情况方式:你听过“流程”方式吗?

简略描述下即:开始处理订单

  • 正常则进入成功情况,入库,结束B 5 _ o $实行
  • 失利则进入失利情况,检测是否从头实行,改变情况为处理订^ = {

上代码

Context 上下文

public class Contexa n ot {

    /**
     * 最大实行次数
     */
    public static final Integer FAIL_NUM = 3;

    /***
     * 失利次数
     */
    private int failNum;

    /**
     * 是否持续实行的标识
     */
    private boolean isAbandon;

    /***
     * 其时情况
     */
    private StateInterface stateInterface;

    public ConC V : z Rtext() {
        this.s3 $ n } ! m .tateInterface = new HandleOrde8 Q W . 9r();
        this.fal  O v H p =ilNum = 1;
        ts 4 % ~his.isAbandon = false;
    }

    /***
     * 处理方法
     */
    public void handle () {
        stateInterface.doAction(this);
    }a c 1

    // 省掉无用代码...
}

处理订单情况

public class HandleOrder implements Sta- 8 - J * WteInterface {

    @Override
    public void[ g f doActiob ^ ~n(Context context) {
        printCurrentState(g F B);

        // do somethings
        int num = (int) (Math.random() * 11);
        if (num >= 8) {
            System.out.println("处理订单结束, 进入成功状W 3 4 L J况...");
            context.setStateInterface({ b knew SuccessOE  8 n @  7 ,rder());
        } else {
            System.out.println("处理订单失利, 进入失利情况...");
            context.setState5 C h 4Interface(new FailOrder());
        }

        CodeUtilsg t j.spilt();
    }

    @Override
    public StateEn6 ^ A t 0 f 3 o +ums getCurrentState() {
        return StateEnums.HANDLE_ORDER;
    }
}

客户端调用方法

public class App {

    public static void main(String[] args) {
        // 仿照从行列中取任务按流程循环实行
        Context context = new Context();
        while (true) {

            // 校验是否为抛弃 | 已结束任务
            if (context.isAbandon()) {
                System.out.println("此条任务不再实行... ");
                break;
            }

            context.handle();
        }
    }
}

测验成果输出:

# 其时情况:订单处理
#@ 1 H *理订单失利, 进入s B I失利情况...
# ------------------------

# 其时情况:处理订单失利
# 订单处理失利... 其时实行次数: 1
# ------------------------

# 其时情况:订单处理
# 处理订单失利, 进入失Y 2 c A M S利情况...
# ------------------------

# 其时情况:处理订单失利
# 订单处理失利... 其时实行次数: 2
# ------------------------

# 其时情况H [ x o | 1 p:订单处理
# 处理订单结束, 进入成功情况...
# --] J m - : 6 n----------------------

# 其时情况:处理订单成功
# 订单处理结束 -K 4 Y |> 进入入库逻辑...
# 入库处理结束
# ------------------------

# 此条任务不再实行... 

假设看着有点模棱两可,主张看完本文} p x q u $ L后,访问专题规划方式开源项目,里面有具体的代码示例,w % t R链接在最下面

“流程” 方式适用的场景

在这样的规划中,与其说是情况的改动,不如说是 “流程” 的改动更为贴切,因而它可以作为许多后台任务的解决方案,尤其是面临许多业务流程场~ m p j ~ 7 2景时,可以极大的提高代码的可维护性: 我只用考虑和d y N我有关的 “流程”

N T g ] ? ( S从的规划准则

  • q # } p k + $ f z装改动:在父级接口中供应 default 方法,子类实现其对应的情况方法即可
  • 多用组合,少? ~ r T ~用继承:情况方式经常和战略方式做对比,它们都是运用组合而非继承增强其改动和才干

什么场景适宜运用情况方式

  • 一个方针的行为取决于它的情况,并且z 0 , J q P它有必要在运行时刻根据情况改n ~ k + A b s动其行为
  • 一个操作中含有巨大的多分支条件句子,且这+ _ C E 4 M I些分支依赖于该方针的情况

最终

附上GOF一书中关于状z V } C $ V u况方式的UML图:

【一同学系列】之情况方式:你听过“流程”方式吗?

相关代码链接

GitHub地址

  • 统筹了《HeadFirst》以及《GOF》两本经典书本中的案例
  • 供应了和睦的阅读教导
【一同学系列】之情况方式:你听过“流程”方式吗?