状态模式


状态模式

状态模式,又称状态机模式,是一种行为型设计模式。它允许对象在其内部状态改变时更改其行为。这种模式是为了减少对象在状态变化时的复杂性。

状态模式可以通过将状态定义为独立的类或对象,将状态转换代码与主对象分离,达到简化代码的目的。当一个对象的状态发生改变时,它通常需要改变它的行为,这就需要在大量代码中进行修改,容易引入 bug。 状态模式通过将状态抽象出来,避免了大量代码的修改,使代码更加简洁和易于维护。

结构

  1. Context(环境):定义客户端感兴趣的接口,并维护一个当前状态对象的引用。
  2. State(状态):定义一个接口,用于封装与 Context 的某个状态相关的行为。
  3. ConcreteState(具体状态):实现状态接口的具体状态对象。同时,它还会处理该状态下的行为。

uml

实现

下面我们以一个投票系统为例,在文章发表一年内,对同一文章进行多次投票,每个投票人只能投一次票。我们采用状态模式实现。

1. 创建 State 接口

public interface VoteState {
    void vote(String user, String voteItem, VoteManager voteManager);
}

2. 创建 Context 类

public class VoteManager {
    private Map<String, String> mapVote = new HashMap<>();
    private Map<String, Integer> mapVoteCount = new HashMap<>();
    private VoteState state = new NormalVoteState();

    public void vote(String user, String voteItem) {
        state.vote(user, voteItem, this);
    }

    public Map<String, String> getMapVote() {
        return mapVote;
    }

    public void setMapVote(Map<String, String> mapVote) {
        this.mapVote = mapVote;
    }

    public Map<String, Integer> getMapVoteCount() {
        return mapVoteCount;
    }

    public void setMapVoteCount(Map<String, Integer> mapVoteCount) {
        this.mapVoteCount = mapVoteCount;
    }

    public void setState(VoteState state) {
        this.state = state;
    }
}

3. 创建具体状态类

3.1. NormalVoteState

该状态下可以投票,并记录投票人和投票结果。

public class NormalVoteState implements VoteState {
    @Override
    public void vote(String user, String voteItem, VoteManager voteManager) {
        voteManager.getMapVote().put(user, voteItem);

        Integer oldCount = voteManager.getMapVoteCount().get(voteItem);
        if (oldCount == null) {
            oldCount = 0;
        }

        voteManager.getMapVoteCount().put(voteItem, oldCount + 1);

        System.out.println("投票成功");
        voteManager.setState(new RepeatVoteState());
    }
}

3.2. RepeatVoteState

该状态下投票人已经投过票,提醒投票人不可重复投票。

public class RepeatVoteState implements VoteState {
    @Override
    public void vote(String user, String voteItem, VoteManager voteManager) {
        String s = voteManager.getMapVote().get(user);
        if (s != null) {
            System.out.println("请不要重复投票");
            return;
        }
        voteManager.getMapVote().put(user, voteItem);

        Integer oldCount = voteManager.getMapVoteCount().get(voteItem);
        if (oldCount == null) {
            oldCount = 0;
        }

        voteManager.getMapVoteCount().put(voteItem, oldCount + 1);

        System.out.println("投票成功");
        voteManager.setState(new SpiteVoteState());
    }
}

3.3. SpiteVoteState

在投票期间,如果某个人恶意投票,那么将禁止其参与投票,并且取消其之前的投票权。

public class SpiteVoteState implements VoteState {
    @Override
    public void vote(String user, String voteItem, VoteManager voteManager) {
        if (voteManager.getMapVote().get(user) != null) {
            System.out.println("你有恶意刷票行为,取消投票资格");
            voteManager.getMapVote().remove(user);
            return;
        }
        voteManager.getMapVote().put(user, voteItem);

        Integer oldCount = voteManager.getMapVoteCount().get(voteItem);
        if (oldCount == null) {
            oldCount = 0;
        }

        voteManager.getMapVoteCount().put(voteItem, oldCount + 1);

        System.out.println("投票成功");
        voteManager.setState(new BlackVoteState());
    }
}

3.4. BlackVoteState

黑名单状态,即进入黑名单之后,将禁止其参与投票。

public class BlackVoteState implements VoteState {
    @Override
    public void vote(String user, String voteItem, VoteManager voteManager) {
        System.out.println("你已经进入黑名单,禁止登录和使用本系统");
    }
}

4. 使用状态模式

public static void main(String[] args) {
    VoteManager voteManager = new VoteManager();
    for (int i = 0; i < 8; i++) {
        voteManager.vote("张三", "投票对象");
    }
}

5. 输出结果

投票成功
请不要重复投票
请不要重复投票
请不要重复投票
请不要重复投票
请不要重复投票
你有恶意刷票行为,取消投票资格
你已经进入黑名单,禁止登录和使用本系统

总结

状态模式是一种简单而有效的设计模式,其核心是定义状态类,将状态转换代码与主对象分离,达到更好的松耦合和可扩展性。在生活开发中,当对象有多种状态且状态之间发生转换时,可以考虑使用此模式。