状态模式
状态模式:允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。
在开始讲状态模式之前,我们先来看一句话:“方法如果过长其实极有可能是有坏味道了”——MartinFowler《重构》,这是MartinFowler对Long Method的看法。
现在我们来看一个场景:用户投票功能,直接看代码:
voteCount=someMethod(); if(voteCount==1) { //正常投票 dosomething } if(voteCount>1 && voteCount<5) { //重复投票,提示不允许重复投票 dosomething } if(voteCount>5 && voteCount<8) { //恶意投票,取消用户投票资格,并取消投票纪录 dosomething } if(voteCount>8) { //将用户拉入黑名单,禁止登录系统 dosomething }
相比很多朋友都见过类似以上代码,或者自己经常写这种代码,甚至结果更长的分支代码,每个分支里面都有成山的逻辑
假设,现在要要增加新功能,如投票超过8次但不足10次的,给个机会,只是禁止登录和使用系统3天,如果再犯,才永久封号,怎么办?就要在一堆if-else中修改源码,再加一个分支,这将出现坏的味道?
我们来看下状态模式的类图:
我们看到已经把投票的各种状态定义出来了,然后把这些状态对应的处理从原来的大杂烩的实现中分离出来,形成独立的状态处理对象,而原来的投票管理的对象就相当于Context了。
为了统一操作这些不同的状态类,定义一个状态接口来约束它们,这样外部就可以面向这个统一的状态接口编程,而无需关心具体的状态类实现了。
这样一来,要修改某种投票情况对应的具体功能处理,只需要直接修改或者扩展某个状态处理类的功能就可以了。而要添加新功能就更简单,直接添加新的状态处理类就可以了。
由于状态是在运行期被改变,因此行为也会在运行期根据状态的改变而改变,看起来,同一个对象,在不同的运行时刻,行为是不一样的,就像是类被修改了一样。
具体实现的时候有个很好的技巧,就是具体的状态处理对象的vote方法的一个参数可以是Context,因为在具体的状态处理类中经常需要获取上下文自身的数据,甚至在必要的时候会回调上下文的方法,因此,通常将上下文自身作为一个参数传递给具体的状态处理类。
部分上下文对象代码:
//持有状态处理对象 private VoteState state=null public void vote(string user,string voteItem) { //根据情况判断,如利用反射或其他方式实例化具体的处理对象 state=someMethod(); //把Context作为参数传给具体处理对象 state.vote(user,voteItem,this); }
部分实际处理对象代码:
public vote(string user,string voteItem,VoteContext context) { //具体处理程序 doSomething //可以视情况访问context或直接回调context的方法 callBackContext }
部分客户端代码:
public static void main(string[] args) { VoteContext context=new VoteContext(); context.vote("user","投票参数"); }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步