行为模式之状态模式

Posted by Night Field's Blog on May 27, 2020

1 概述

状态模式(state Pattern)是行为模式之一,一般用在对象的行为依赖于内部状态的场景。

2 状态模式

考虑一个场景:对象有状态,而且根据状态不同,对象的行为也会不同。对于该情况,最简单的方式是,对状态做if-else或者swith-case判断,根据状态调用相应的行为。但是这种做法,把对象和状态耦合在了一起,难于扩展和维护。更好的做法是,用状态模式将状态抽离出来形成一个接口,而目标对象依赖该状态接口,来完成对应的行为,由此达到解耦的目的。 状态模式策略模式非常相像,可视为策略模式的扩展。两者都基于组合机制,通过将部分工作委派给另外的对象来改变其在不同情景下的行为。不同的是,策略模式中这些对象相互之间完全独立,不知道彼此的存在。但状态模式没有限制具体状态之间的依赖,且允许它们自行改变在不同情景下的状态。

3 案例

结合例子,来进一步理解状态模式。 一个人的战斗力,在饥饿状态和发怒状态下是不同的,所以“战斗的行为”依赖于“身体的状态”,最简单的做法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Fighter {
    private String mode = "normal";
    public void setMode(String mode) {
        this.mode = mode;
    }
    public void fight() {
        if (mode.equalsIgnoreCase("angry")) {
            System.out.println("Fighting with power 200 on angry mode!!!");
        }
        else if (mode.equalsIgnoreCase("hungry")) {
            System.out.println("Fighting with power 50 on mode hungry...");
        }
        else {
            System.out.println("Fighting with power 100 normal mode!");
        }
    }
}

简单的if-else块,根据状态控制行为。但是这种写法不容易维护,每当状态需要修改/新增/删除,都需要改动Fighter类;而且当状态越来越多的时候,方法体会变得越来越庞大,可读性很低。下面看看用状态模式如何操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public interface State {
    void fight();
}
public class NormalState implements State {
    @Override
    public void fight() {
        System.out.println("Fighting with power 100 normal mode!");
    }
}
public class HungryState implements State {
    @Override
    public void fight() {
        System.out.println("Fighting with power 50 on mode hungry...");
    }
}
public class AngryState implements State {
    @Override
    public void fight() {
        System.out.println("Fighting with power 200 on angry mode!!!");
    }
}

public class Fighter implements State {
    private State state = new NormalState();

    public void setState(State state) {
        this.state = state;
    }
    public State getState() {
        return state;
    }
    @Override
    public void fight() {
        state.fight();
    }
}

public class Test {
    public static void main(String[] args) {
        Fighter luffy = new Fighter();
        State hungryState = new HungryState();
        State angryState = new AngryState();

        luffy.fight();

        luffy.setState(hungryState);
        luffy.fight();

        luffy.setState(angryState);
        luffy.fight();
    }
}

输出:

1
2
3
Fighting with power 100 normal mode!
Fighting with power 50 on mode hungry...
Fighting with power 200 on angry mode!!!

我们将“状态”从对象中抽离出来,做成一个单独的接口State,并将对象的行为放到State的实现类中。这种模式,无论是扩展性还是可维护性,都高于前面那种方式,这就是状态模式的威力。

4 总结

状态模式使得对象与其状态分离解耦,使得代码逻辑更加灵活健壮。当对象的行为依赖于内部状态时,不要一昧地用if-else,可以考虑使用状态模式

文中例子的github地址