1.场景模拟
考虑一个在线投票的应用,分为四种情况
正常投票
正常投票以后还继续重复投票
用户恶意投票
黑名单用户
2.不用模式的解决方案
package demo17.state.example1; import java.util.*; /** * 投票管理 */ public class VoteManager { /** * 记录用户投票的结果,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>(); /** * 投票 * @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){ //正常投票 //记录到投票记录中 mapVote.put(user, voteItem); System.out.println("恭喜你投票成功"); }else if(oldVoteCount>1 && oldVoteCount<5){ //重复投票 //暂时不做处理 System.out.println("请不要重复投票"); }else if(oldVoteCount >= 5 && oldVoteCount<8){ //恶意投票 //取消用户的投票资格,并取消投票记录 String s = mapVote.get(user); if(s!=null){ mapVote.remove(user); } System.out.println("你有恶意刷票行为,取消投票资格"); }else if(oldVoteCount>=8){ //黑名单 //记入黑名单中,禁止登录系统了 System.out.println("进入黑名单,将禁止登录和使用本系统"); } } } ************************************************************************************************ package demo17.state.example1; public class Client { public static void main(String[] args) { VoteManager vm = new VoteManager(); for (int i = 0; i < 8; i++) { vm.vote("u1", "A"); } } }
3.问题所在(问题分析)
上述代码看起来很简单,实际上有很大的不妥之处,比如要添加新的功能,那怎么办呢?要在对应的一大堆的控制代码中找出需要的部分,然后进行修改,是很麻烦的。那么怎么很容易的扩展vote()这个方法呢?
由于刚刚学过了策略模式,很容易就想到要用策略模式来实现,因为感觉这里用策略模式正好。实际上则不然,这里不用策略模式实现,是因为策略模式重心在于分离算法,然后选择实现,大家可以发现,这个案例,没有什么需要计算的算法,都是一些相同的行为的次数的递增罢了,策略模式是不同行为的实现,针对不同的对象使用不同的策略,这里很明显不是,这里是相同的行为的次数的不同,导致结果不同,抽象化以后就是:对象的状态不同,导致结果不同,大家要仔细区分清楚。当然,你看这两个模式的客户端实现就能看出不同来。
那么,不用策略模式,用什么模式呢?
4.使用状态模式解决问题
4.1状态模式定义
允许一个对象在其内部状态改变时候,改变它的行为,对象看起来似乎修改了他的类。
4.2状态模式的结构和说明
结构图跟策略模式基本上是一样的,如下
4.3状态模式示例代码
package demo17.state.example2; /** * 封装与Context的一个特定状态相关的行为 */ public interface State { /** * 状态对应的处理 * * @param sampleParameter * 示例参数,说明可以传入参数,具体传入 什么样的参数,传入几个参数,由具体应用来具体分析 */ public void handle(String sampleParameter); } ********************************************************************** package demo17.state.example2; /** * 实现一个与Context的一个特定状态相关的行为 */ public class ConcreteStateA implements State { public void handle(String sampleParameter) { // 实现具体的处理 } } ********************************************************************** package demo17.state.example2; /** * 实现一个与Context的一个特定状态相关的行为 */ public class ConcreteStateB implements State { public void handle(String sampleParameter) { // 实现具体的处理 } } ********************************************************************** package demo17.state.example2; /** * 定义客户感兴趣的接口,通常会维护一个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); } }
5.使用状态模式重写实例代码
5.1投票接口
package demo17.state.example3; /** * 封装一个投票状态相关的行为 */ public interface VoteState { /** * 处理状态对应的行为 * @param user 投票人 * @param voteItem 投票项 * @param voteManager 投票上下文,用来在实现状态对应的功能处理的时候, * 可以回调上下文的数据 */ public void vote(String user,String voteItem,VoteManager voteManager); }
5.2四个实现
package demo17.state.example3; public class NormalVoteState implements VoteState{ public void vote(String user, String voteItem, VoteManager voteManager) { //正常投票 //记录到投票记录中 voteManager.getMapVote().put(user, voteItem); System.out.println("恭喜你投票成功"); } } package demo17.state.example3; public class RepeatVoteState implements VoteState{ public void vote(String user, String voteItem, VoteManager voteManager) { //重复投票 //暂时不做处理 System.out.println("请不要重复投票"); } } package demo17.state.example3; 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("你有恶意刷票行为,取消投票资格"); } } package demo17.state.example3; public class BlackWarnVoteState implements VoteState{ public void vote(String user, String voteItem, VoteManager voteManager) { //待进黑名单警告状态 System.out.println("禁止登录和使用系统3天"); } }
5.3投票管理
package demo17.state.example3; import java.util.*; /** * 投票管理 */ 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); } }
5.4客户端不变
package demo17.state.example3; public class Client { public static void main(String[] args) { VoteManager vm = new VoteManager(); for(int i=0;i<8;i++){ vm.vote("u1", "A"); } } }
6.状态模式讲解
6.1状态模式要点
状态和行为:对象的状态就是指对象实例的属性的值;而行为指的是对象的功能。
状态模式的功能:分离状态的行为,通过维护状态的变化,来调用不同状态对应不同的功能。
行为的平行性:注意是平行性,而不是平等性。
6.2状态模式的调用顺序图
6.3状态模式优缺点
优点:
简化应用逻辑控制
更好的分离状态和行为
更好的扩展性
显示化进行状态转换
缺点:
一个状态引入一个对应的状态处理类,会使得程序引入太多的状态类,使程序变得杂乱。
6.4状态模式本质
根据状态来分离和选择行为