原文:https://blog.csdn.net/liaodehong/article/details/52079502
模式簡介
允許一個(gè)對(duì)象在其內(nèi)部狀態(tài)改變時(shí)改變它的行為。對(duì)象看起來似乎修改了它的類胎挎,(State Pattern)是設(shè)計(jì)模式的一種若债,屬于行為模式壤玫。
定義
(源于Design Pattern):當(dāng)一個(gè)對(duì)象的內(nèi)在狀態(tài)改變時(shí)允許改變其行為杆融,這個(gè)對(duì)象看起來像是改變了其類体箕。
狀態(tài)模式主要解決的是當(dāng)控制一個(gè)對(duì)象狀態(tài)的條件表達(dá)式過于復(fù)雜時(shí)的情況痕钢。把狀態(tài)的判斷邏輯轉(zhuǎn)移到表示不同狀態(tài)的一系列類中香到,可以把復(fù)雜的判斷邏輯簡化鱼冀。
模式中的角色
1 上下文環(huán)境(Context):它定義了客戶程序需要的接口并維護(hù)一個(gè)具體狀態(tài)角色的實(shí)例,將與狀態(tài)相關(guān)的操作委托給當(dāng)前的Concrete State對(duì)象來處理悠就。
2 抽象狀態(tài)(State):定義一個(gè)接口以封裝使用上下文環(huán)境的的一個(gè)特定狀態(tài)相關(guān)的行為千绪。
3 具體狀態(tài)(Concrete State):實(shí)現(xiàn)抽象狀態(tài)定義的接口。
狀態(tài)模式的類圖
這里來看看狀態(tài)模式的標(biāo)準(zhǔn)代碼梗脾;
首先我們先定義一個(gè)State抽象狀態(tài)類荸型,里面定義了一個(gè)接口以封裝 與Context的一個(gè)特定狀態(tài)相關(guān)的行為;
/**
* 抽象狀態(tài)類
* @author gh
*
*/
public abstract class State {
public abstract void Handle(Context context);
}
接著再去聲明一個(gè)ConcreteState具體狀態(tài)類炸茧,每一個(gè)子類實(shí)現(xiàn)一個(gè)與Context的一個(gè)狀態(tài)的相關(guān)的行為瑞妇。
public class ConcreteStateA extends State{
@Override
public void Handle(Context context) {
context.setState(new ConcreteStateB()); //設(shè)置A的下一個(gè)狀態(tài)是B
}
}
class ConcreteStateB extends State{
@Override
public void Handle(Context context) {
context.setState(new ConcreteStateA()); //設(shè)置B的下一個(gè)狀態(tài)是A
}
}
Context類,維護(hù)一個(gè)ConcreteState子類的實(shí)例梭冠,這個(gè)實(shí)例定義當(dāng)前的狀態(tài)
/**
* 定義當(dāng)前的狀態(tài)
* @author gh
*
*/
public class Context {
State state;
public Context(State state) { //定義Context的初始狀態(tài)
super();
this.state = state;
}
public State getState() {
return state;
}
public void setState(State state) {
this.state = state;
System.out.println("當(dāng)前狀態(tài)為"+state);
}
public void request(){
state.Handle(this); //對(duì)請(qǐng)求做處理并且指向下一個(gè)狀態(tài)
}
}
提到狀態(tài)模式辕狰,讓我想到了工作流,工作流就是控制一個(gè)一個(gè)的節(jié)點(diǎn)狀態(tài)來實(shí)現(xiàn)節(jié)點(diǎn)的跳轉(zhuǎn)控漠,最后來控制流程蔓倍。
如果上面發(fā)起了一個(gè)請(qǐng)假流程,這個(gè)時(shí)候第一個(gè)節(jié)點(diǎn)就是部門領(lǐng)導(dǎo)審核盐捷,部門領(lǐng)導(dǎo)審核通過會(huì)繼續(xù)往下走偶翅,如果不通過那么有兩種狀態(tài),一種是直接駁回請(qǐng)求碉渡,領(lǐng)導(dǎo)說聚谁,項(xiàng)目最近很急,任何人都不能請(qǐng)假爆价,還有一種是你寫的請(qǐng)假申請(qǐng)單不對(duì)垦巴,要退回整改重新寫。審核通過后就進(jìn)入下一個(gè)節(jié)點(diǎn)铭段,人力資源部門審核骤宣,當(dāng)然人力資源也可以駁回請(qǐng)求,或者要你重新整改序愚,人力資源審核通過之后就可以休假了憔披,這個(gè)時(shí)候還可以選擇是否發(fā)送Email。
/**
* 節(jié)點(diǎn)接口
* @author gh
*
*/
public abstract class Node {
private static String name; //當(dāng)前節(jié)點(diǎn)名稱
//節(jié)點(diǎn)跳轉(zhuǎn)
public abstract void nodeHandle(FlowContext context);
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
相當(dāng)于State類爸吮,這里維護(hù)一個(gè)節(jié)點(diǎn)名稱芬膝。
/**
* 領(lǐng)導(dǎo)節(jié)點(diǎn)
*
* @author gh
*
*/
public class LeadNode extends Node {
@Override
public void nodeHandle(FlowContext context) {
//根據(jù)當(dāng)前流程的狀態(tài),來控制流程的走向
//先判斷流程是否結(jié)束
if(!context.isFlag()){
System.out.println(context.getMessage()); //先讀取申請(qǐng)的內(nèi)容
if(context!=null&&3==context.getStatus()){ //只有出于已經(jīng)申請(qǐng)的狀態(tài)才又部門領(lǐng)導(dǎo)審核
//設(shè)置當(dāng)前節(jié)點(diǎn)的名稱
setName("張經(jīng)理");
//加上審核意見
context.setMessage(context.getMessage()+getName()+"審核通過;");
//審核通過
context.setStatus(0); //審核通過并指向下一個(gè)節(jié)點(diǎn)
context.setNode(new HrNode());
context.getNode().nodeHandle(context);
}
}else{
System.err.println("流程已經(jīng)結(jié)束");
}
}
}
這里創(chuàng)建了一個(gè)領(lǐng)導(dǎo)節(jié)點(diǎn)形娇,用來維護(hù)領(lǐng)導(dǎo)審核的流程锰霜,審核通過會(huì)交給HR審核;
public class HrNode extends Node {
@Override
public void nodeHandle(FlowContext context) {
//先判斷流程是否結(jié)束
if(!context.isFlag()){
// 根據(jù)當(dāng)前流程的狀態(tài)桐早,來控制流程的走向
if (context != null &&
0 == context.getStatus()) { //只有上一級(jí)審核通過后才能輪到HR審核
// 設(shè)置當(dāng)前節(jié)點(diǎn)的名稱
setName("HR李");
//讀取上一級(jí)的審核內(nèi)容并加上自己的意見
System.out.println(context.getMessage()+getName()+"審核通過");
// 審核通過
context.setStatus(0); //HR審核通過并指向下一個(gè)節(jié)點(diǎn) ,如果沒有下一個(gè)節(jié)點(diǎn)就把狀態(tài)設(shè)置為終結(jié)
context.setFlag(true);
}
}else{
System.out.println("流程已經(jīng)結(jié)束");
}
}
}
這里HR審核通過并把節(jié)點(diǎn)設(shè)置為完結(jié)狀態(tài)癣缅;
/**
* 流程控制
*
* @author gh
*
*/
public class FlowContext {
private boolean flag; // 代表流程是否結(jié)束
/**
* 流程狀態(tài) 0:通過 1:駁回 2.退回整改 3.已申請(qǐng)
*
*/
private int status;
private String message; // 消息
private Node node; // 節(jié)點(diǎn)信息
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Node getNode() {
return node;
}
public void setNode(Node node) {
this.node = node;
}
public static boolean start(FlowContext context) {
Node node = new LeadNode();
context.setNode(node); // 設(shè)置初始節(jié)點(diǎn)
context.setStatus(3); // 設(shè)置狀態(tài)為申請(qǐng)中
context.getNode().nodeHandle(context); // 發(fā)起請(qǐng)求
// 最后要知道是否申請(qǐng)成功
//判斷當(dāng)前是最后一個(gè)節(jié)點(diǎn)并且審核通過,而且流程結(jié)束
if("HR李".equals(node.getName())&&0==context.getStatus()&&context.isFlag()){
System.out.println("審核通過,流程結(jié)束");
return true;
}else{
System.out.println("審核未通過哄酝,流程已經(jīng)結(jié)束");
return false;
}
}
public FlowContext() {
super();
}
}
這里維護(hù)一個(gè)流程控制類友存,它會(huì)在HR和LEAD節(jié)點(diǎn)之后傳遞,并分別由他們?nèi)ゾS護(hù)各自的節(jié)點(diǎn)陶衅。
最后寫一個(gè)測試類測試一下:
public static void main(String[] args) {
FlowContext context=new FlowContext();
context.setMessage("本人王小二屡立,因?yàn)槭患依镉惺虑椋砸嗾?qǐng)三天假搀军,希望公司能夠?qū)徍送ㄟ^");
context.start(context);
}
打印結(jié)果如下
本人王小二膨俐,因?yàn)槭患依镉惺虑椋砸嗾?qǐng)三天假罩句,希望公司能夠?qū)徍送ㄟ^
本人王小二吟策,因?yàn)槭患依镉惺虑椋砸嗾?qǐng)三天假的止,希望公司能夠?qū)徍送ㄟ^張經(jīng)理審核通過;HR李審核通過
審核通過,流程結(jié)束檩坚;
上面這個(gè)例子只是很簡單的模仿了一下工作流控制狀態(tài)的跳轉(zhuǎn)。狀態(tài)模式最主要的好處就是把狀態(tài)的判斷與控制放到了其服務(wù)端的內(nèi)部诅福,使得客戶端不需要去寫很多代碼判斷匾委,來控制自己的節(jié)點(diǎn)跳轉(zhuǎn),而且這樣實(shí)現(xiàn)的話氓润,我們可以把每個(gè)節(jié)點(diǎn)都分開來處理赂乐,當(dāng)流程流轉(zhuǎn)到某個(gè)節(jié)點(diǎn)的時(shí)候,可以去寫自己的節(jié)點(diǎn)流轉(zhuǎn)方法咖气。當(dāng)然狀態(tài)模式的缺點(diǎn)也很多挨措,比如類的耦合度比較高挖滤,基本上三個(gè)類要同時(shí)去寫,而且會(huì)創(chuàng)建很多的節(jié)點(diǎn)類浅役。