`

状态模式2

 
阅读更多

18.2  解决方案

18.2.1  状态模式来解决

用来解决上述问题的一个合理的解决方案就是状态模式。那么什么是状态模式呢?

(1)状态模式定义

 

(2)应用状态模式来解决的思路

       仔细分析上面的问题,会发现,那几种用户投票的类型,就相当于是描述了人员的几种投票状态,而各个状态和对应的功能处理具有很强的对应性,有点类似于“一个萝卜一个坑”,各个状态下的处理基本上都是不一样的,也不存在可以相互替换的可能。

       为了解决上面提出的问题,很自然的一个设计就是首先把状态和状态对应的行为从原来的大杂烩代码中分离出来,把每个状态所对应的功能处理封装在一个独立的类里面,这样选择不同处理的时候,其实就是在选择不同的状态处理类。

       然后为了统一操作这些不同的状态类,定义一个状态接口来约束它们,这样外部就可以面向这个统一的状态接口编程,而无需关心具体的状态类实现了。

       这样一来,要修改某种投票情况所对应的具体功能处理,那就是直接修改或者扩展某个状态处理类的功能就可以了。而要添加新的功能就更简单,直接添加新的状态处理类就可以了,当然在使用Context的时候,需要设置使用这个新的状态类的实例。

18.2.2  模式结构和说明

状态模式的结构如图18.1所示:

 

图18.1  状态模式结构示意图

Context

       环境,也称上下文,通常用来定义客户感兴趣的接口,同时维护一个来具体处理当前状态的实例对象。

State

       状态接口,用来封装与上下文的一个特定状态所对应的行为。

ConcreteState

       具体实现状态处理的类,每个类实现一个跟上下文相关的状态的具体处理。

18.2.3  状态模式示例代码

(1)首先来看状态接口,示例代码如下:

/**

 * 封装与Context的一个特定状态相关的行为

 */

public interface State {

    /**

     * 状态对应的处理

     * @param sampleParameter 示例参数,说明可以传入参数,具体传入

     *             什么样的参数,传入几个参数,由具体应用来具体分析

     */

    public void handle(String sampleParameter);

}

(2)再来看看具体的状态实现,目前具体的实现ConcreteStateA和ConcreteStateB示范的是一样的,只是名称不同,示例代码如下:

/**

 * 实现一个与Context的一个特定状态相关的行为

 */

public class ConcreteStateA implements State {

    public void handle(String sampleParameter) {

       //实现具体的处理

    }

}

/**

 * 实现一个与Context的一个特定状态相关的行为

 */

public class ConcreteStateB implements State {

    public void handle(String sampleParameter) {

       //实现具体的处理

    }

}

(3)再来看看上下文的具体实现,上下文通常用来定义客户感兴趣的接口,同时维护一个具体的处理当前状态的实例对象。示例代码如下:

/**

 * 定义客户感兴趣的接口,通常会维护一个State类型的对象实例

 */

public class Context {

    /**

     * 持有一个State类型的对象实例

     */

    private State state;

    /**

     * 设置实现State的对象的实例

     * @param state 实现State的对象的实例

     */

    public void setState(State state) {

       this.state = state;

    }

    /**

     * 用户感兴趣的接口方法

     * @param sampleParameter 示意参数

     */

    public void request(String sampleParameter) {

       //在处理中,会转调state来处理

       state.handle(sampleParameter);

    }

}

18.2.4  使用状态模式重写示例

       看完了上面的状态模式的知识,有些朋友跃跃欲试,打算使用状态模式来重写前面的示例,要使用状态模式,首先就需要把投票过程的各种状态定义出来,然后把这些状态对应的处理从原来大杂烩的实现中分离出来,形成独立的状态处理对象。而原来的投票管理的对象就相当于Context了。

       把状态对应的行为分离出去过后,怎么调用呢?

       按照状态模式的示例,是在Context中,处理客户请求的时候,转调相应的状态对应的具体的状态处理类来进行处理。

       那就引出下一个问题:那么这些状态怎么变化呢?

       看原来的实现,就是在投票方法里面,根据投票的次数进行判断,并维护投票类型的变化。那好,也依葫芦画瓢,就在投票方法里面来维护状态变化。

       这个时候的程序结构如图18.2所示:

 

图18.2  状态模式的示例程序机构示意图

(1)先来看状态接口的代码实现,示例代码如下:

/**

 * 封装一个投票状态相关的行为

 */

public interface VoteState {

    /**

     * 处理状态对应的行为

     * @param user 投票人

     * @param voteItem 投票项

     * @param voteManager 投票上下文,用来在实现状态对应的功能处理的时候,

     *                    可以回调上下文的数据

     */

    public void vote(String user,String voteItem

,VoteManager voteManager);

}

(2)定义了状态接口,那就该来看看如何实现各个状态对应的处理了,现在的实现很简单,就是把原来的实现从投票管理类里面分离出来就可以了。先看正常投票状态对应的处理,示例代码如下:

public class NormalVoteState implements VoteState{

    public void vote(String user, String voteItem

,VoteManager voteManager) {

       //正常投票

       //记录到投票记录中

       voteManager.getMapVote().put(user, voteItem);

       System.out.println("恭喜你投票成功");

    }

}

       接下来看看重复投票状态对应的处理,示例代码如下:

public class RepeatVoteState implements VoteState{

    public void vote(String user, String voteItem

,VoteManager voteManager) {

       //重复投票

       //暂时不做处理

       System.out.println("请不要重复投票");

    }

}

接下来看看恶意投票状态对应的处理,示例代码如下:

public class SpiteVoteState implements VoteState{

    public void vote(String user, String voteItem

,VoteManager voteManager) {

       //恶意投票

       //取消用户的投票资格,并取消投票记录

       String s = voteManager.getMapVote().get(user);

       if(s!=null){

           voteManager.getMapVote().remove(user);

       }

       System.out.println("你有恶意刷票行为,取消投票资格");

    }

}

接下来看看黑名单状态对应的处理,示例代码如下:

public class BlackVoteState implements VoteState{

    public void vote(String user, String voteItem

,VoteManager voteManager) {

       //黑名单

       //记入黑名单中,禁止登录系统了

       System.out.println("进入黑名单,将禁止登录和使用本系统");

    }

}

(3)定义好了状态接口和状态实现,看看现在的投票管理,相当于状态模式中的上下文,相对而言,它的改变如下:

  • 添加持有状态处理对象
  • 添加能获取记录用户投票结果的Map的方法,各个状态处理对象,在进行状态对应的处理的时候,需要获取上下文中的记录用户投票结果的Map数据
  • 在vote()方法实现里面,原来判断投票类型就变成了判断投票的状态,而原来每种投票类型对应的处理,现在已经封装到对应的状态对象里面去了,因此直接转调对应的状态对象的方法即可

示例代码如下:

/**

 * 投票管理

 */

public class VoteManager {

    /**

     * 持有状态处理对象

     */

    private VoteState state = null;

    /**

     * 记录用户投票的结果,Map<String,String>对应Map<用户名称,投票的选项>

     */

    private Map<String,String> mapVote =

new HashMap<String,String>();

    /**

     * 记录用户投票次数,Map<String,Integer>对应Map<用户名称,投票的次数>

     */

    private Map<String,Integer> mapVoteCount =

new HashMap<String,Integer>();

    /**

     * 获取记录用户投票结果的Map

     * @return 记录用户投票结果的Map

     */

    public Map<String, String> getMapVote() {

       return mapVote;

    }

   

    /**

     * 投票

     * @param user 投票人,为了简单,就是用户名称

     * @param voteItem 投票的选项

     */

    public void vote(String user,String voteItem){

       //1:先为该用户增加投票的次数

       //先从记录中取出已有的投票次数

       Integer oldVoteCount = mapVoteCount.get(user);

       if(oldVoteCount==null){

           oldVoteCount = 0;

       }

       oldVoteCount = oldVoteCount + 1;

       mapVoteCount.put(user, oldVoteCount); 

       //2:判断该用户投票的类型,就相当于是判断对应的状态

       //到底是正常投票、重复投票、恶意投票还是上黑名单的状态

       if(oldVoteCount==1){

           state = new NormalVoteState();

       }else if(oldVoteCount>1 && oldVoteCount<5){

           state = new RepeatVoteState();

       }else if(oldVoteCount >= 5 && oldVoteCount<8){

           state = new SpiteVoteState();

       }else if(oldVoteCount>=8){

           state = new BlackVoteState();

       }

 

       //然后转调状态对象来进行相应的操作

       state.vote(user, voteItem, this);

    }

}

(4)该写个客户端来测试一下了,经过这么修改过后,好用吗?试试看就知道了。客户端没有任何的改变,跟前面实现的一样,示例代码如下:

public class Client {

    public static void main(String[] args) {

       VoteManager vm = new VoteManager();

       for(int i=0;i<8;i++){

           vm.vote("u1", "A");

       }

    }

}

       运行一下试试吧,结果应该是跟前面一样的,也就是说都是实现一样的功能,只是采用了状态模式来实现。测试结果如下:

恭喜你投票成功

请不要重复投票

请不要重复投票

请不要重复投票

你有恶意刷票行为,取消投票资格

你有恶意刷票行为,取消投票资格

你有恶意刷票行为,取消投票资格

进入黑名单,将禁止登录和使用本系统

从上面的示例可以看出,状态的转换基本上都是内部行为,主要在状态模式内部来维护。比如对于投票的人员,任何时候他的操作都是投票,但是投票管理对象的处理却不一定一样,会根据投票的次数来判断状态,然后根据状态去选择不同的处理。

 

ps:转载私塾在线

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics