一、状态模式简介
1. 什么是状态模式
状态模式(State Pattern) 属于行为型设计模式的一种,其定义为:Allow an object to alter its behavior when its internal state changes . The object will appear to change its class (当一个对象内在状态改变时允许其改变其行为,这个对象看起来就像是改变了其类)。状态模式的核心思想就是封装,状态的变更引起了行为的变更,从外部看起来就好像这个对象对应的类发生了变换一样。
2. 业务场景
大家平时的工作中难免的会使用很多审批相关的功能,比如加班审批,外出审批,请假审批等等。不难发现这些这几类审批都有共同的特点:随着审批流程的进行,我们提交的审批状态也在发生改变。如果我们想要模拟这种场景,使用今天要学习的状态模式就是一个很好的选择。
我们的程序员小明今日起床,发现所在城市的PM2.5严重爆表,虽然小明是个敬业的打工人,但是这种情况下出去跑毒很明显不是一个正确的选择,于是小明想要请一个小时的假,晚点上班。
但是由于小明所在的公司并没有办公用的OA软件,所以小明需要给他的领导们一个一个打电话申请。虽然最后请假审批通过了,但是热爱工作的小明还是陷入了沉思。显然小明此时已经认识到了有一个职能的请假APP是多么的重要,于是小明便着手开始为公司设计一款请假OA系统。
既然要设计一个请假系统,那么小明要做的第一件事就是整理一个完整的请假审批所需要的流程。于是小明画了第一版请假流程图:
二、状态模式的实现
1. 设计思路
当我们有了设计流程的时候,我们需要做的就是将流程转换为类图。首先我们在不考虑性能和工作量的情况下,简单的设计出第一版类图:
这里小明的设计思路是再客户端创建一个sendApproval方法用来提交请假审批,然后通过创建不同的审批对象来控制审批状态的变化。虽然这种方式实现简单,但是由于没有集中的针对审批状态进行管理,可能无法很好的进行权限控制,这样就存在越级审批的可能性。
既然知道了问题,小明就继续的对类图进行优化:
在类图中,定义一个ApprovalIterface接口,声明了一个受保护的对象Context变量,这个是串联各个状态的封装类。封装的目的很明显,就是审批单的内部状态被保护不被调用类知晓,也就是遵守了迪米特法则,并且根据不同的审批部门制定了不同的实现类,承担的是状态的产生以及状态改变的过度,总结来讲一共有三个角色参与了这次审批:
- 抽象状态角色(State): 通常是接口或抽象类,负责对象状态定义,并且封装环境角色以实现状态的切换。
- 具体状态角色(ConcreteState):每一个具体状态必须完成两个职责,本状态的行为管理及趋向状态处理通俗来讲就是当前状态要做的事情及过渡到其他状态。
2. 代码实现
理论的准备完成后,小明就着手开始编写相关代码了。
1. 首先是状态接口的设计
public interface ApprovalInterface {
void checkAdopt(Context context);
void checkUnAdopt(Context context);
}
2. 接下来我们要做的是把三个实现类编写出来
// 组长审批
public class GroupApproval implements ApprovalInterface {
@Override
public void checkAdopt(Context context) {
System.out.println("组长审批通过");
context.setApproval(Context.DEPARTMENT_APPROVAL);
}
@Override
public void checkUnAdopt(Context context) {
System.out.println("组长审批未通过,审批流程结束");
}
}
//部门审批
public class DepartmentApproval implements ApprovalInterface{
@Override
public void checkAdopt(Context context) {
System.out.println("部门审批通过");
context.setApproval(Context.CEO_APPROVAL);
}
@Override
public void checkUnAdopt(Context context) {
System.out.println("部门审批未通过,审批流程结束");
}
}
//CEO审批
public class CEOApproval implements ApprovalInterface {
@Override
public void checkAdopt(Context context) {
System.out.println("CEO审批通过,审批结束");
}
@Override
public void checkUnAdopt(Context context) {
System.out.println("CEO审批未通过,审批结束");
}
}
3. 第三步我们要创建审批单对象(环境对象)
public class Context {
private ApprovalInterface approval;
public final static ApprovalInterface GROUP_APPROVAL = new GroupApproval();
public final static ApprovalInterface DEPARTMENT_APPROVAL = new DepartmentApproval();
public final static ApprovalInterface CEO_APPROVAL = new CEOApproval();
public Context() {
super();
approval = GROUP_APPROVAL;
}
public void checkAdopt() {
approval.checkAdopt(this);
}
public void checkUnAdopt() {
approval.checkUnAdopt(this);
}
public ApprovalInterface getApproval() {
return approval;
}
public void setApproval(ApprovalInterface approval) {
this.approval = approval;
}
}
4. 最后提供测试代码
public class Client {
public static void main(String[] args) {
sendApproval();
}
private static void sendApproval() {
Context context = new Context();
context.checkAdopt();
context.checkAdopt();
context.checkAdopt();
}
}
测试结果:
好了,一个简单的请假审批OA就实现了,当然这段代码中还有很多权限控制的地方可以优化,感兴趣的同学可以在评论区交作业哦~
三、状态模式总结
1. 状态模式的特点
优点:
- 封装了转换规则,便于更见方便的转换和增加新的规则。
- 避免了过多的条件判断
- 遵循设计原则
- 封装型好
缺点
- 会造成类的膨胀
2. 状态模式的使用场景
- 行为随着状态改变的场景,例如订单支付系统,审批流,权限控制等等。
- 重构拥有过多的判断语句的代码。
四、结语
今天的代理模式讲解就全部结束了,设计模式的相关代码已经在gitee上收录,有需求的小伙伴可以直接拿走:
有疑问的小伙伴欢迎评论区留言或者私信博主,博主会在第一时间为你解答。
码字不易,感到有收获的小伙伴记得要关注博主一键三连,不要当白嫖怪哦~
博主在这里祝大家可以在新的一年升职加薪,走上人生巅峰!