状态模式

状态模式:允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。

在开始讲状态模式之前,我们先来看一句话:“方法如果过长其实极有可能是有坏味道了”——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","投票参数");
}

 

posted @   何塞穆里尼奥  阅读(9)  评论(0编辑  收藏  举报
努力加载评论中...
点击右上角即可分享
微信分享提示