1.概述
【例】通過(guò)按鈕來(lái)控制一個(gè)電梯的狀態(tài),一個(gè)電梯有開(kāi)門(mén)狀態(tài),關(guān)門(mén)狀態(tài),停止?fàn)顟B(tài),運(yùn)行狀態(tài)。每一種狀態(tài)改變,都有可能要根據(jù)其他狀態(tài)來(lái)更新處理。例如,如果電梯門(mén)現(xiàn)在處于運(yùn)行時(shí)狀態(tài),就不能進(jìn)行開(kāi)門(mén)操作,而如果電梯門(mén)是停止?fàn)顟B(tài),就可以執(zhí)行開(kāi)門(mén)操作。
public interface ILift {
//電梯的4個(gè)狀態(tài)
//開(kāi)門(mén)狀態(tài)
public final static int OPENING_STATE = 1;
//關(guān)門(mén)狀態(tài)
public final static int CLOSING_STATE = 2;
//運(yùn)行狀態(tài)
public final static int RUNNING_STATE = 3;
//停止?fàn)顟B(tài)
public final static int STOPPING_STATE = 4;
//設(shè)置電梯的狀態(tài)
public void setState(int state);
//電梯的動(dòng)作
public void open();
public void close();
public void run();
public void stop();
}
public class Lift implements ILift {
private int state;
@Override
public void setState(int state) {
this.state = state;
}
//執(zhí)行關(guān)門(mén)動(dòng)作
@Override
public void close() {
switch (this.state) {
case OPENING_STATE:
System.out.println("電梯關(guān)門(mén)了。。。");//只有開(kāi)門(mén)狀態(tài)可以關(guān)閉電梯門(mén),可以對(duì)應(yīng)電梯狀態(tài)表來(lái)看
this.setState(CLOSING_STATE);//關(guān)門(mén)之后電梯就是關(guān)閉狀態(tài)了
break;
case CLOSING_STATE:
//do nothing //已經(jīng)是關(guān)門(mén)狀態(tài),不能關(guān)門(mén)
break;
case RUNNING_STATE:
//do nothing //運(yùn)行時(shí)電梯門(mén)是關(guān)著的,不能關(guān)門(mén)
break;
case STOPPING_STATE:
//do nothing //停止時(shí)電梯也是關(guān)著的,不能關(guān)門(mén)
break;
}
}
//執(zhí)行開(kāi)門(mén)動(dòng)作
@Override
public void open() {
switch (this.state) {
case OPENING_STATE://門(mén)已經(jīng)開(kāi)了,不能再開(kāi)門(mén)了
//do nothing
break;
case CLOSING_STATE://關(guān)門(mén)狀態(tài),門(mén)打開(kāi):
System.out.println("電梯門(mén)打開(kāi)了。。。");
this.setState(OPENING_STATE);
break;
case RUNNING_STATE:
//do nothing 運(yùn)行時(shí)電梯不能開(kāi)門(mén)
break;
case STOPPING_STATE:
System.out.println("電梯門(mén)開(kāi)了。。。");//電梯停了,可以開(kāi)門(mén)了
this.setState(OPENING_STATE);
break;
}
}
//執(zhí)行運(yùn)行動(dòng)作
@Override
public void run() {
switch (this.state) {
case OPENING_STATE://電梯不能開(kāi)著門(mén)就走
//do nothing
break;
case CLOSING_STATE://門(mén)關(guān)了,可以運(yùn)行了
System.out.println("電梯開(kāi)始運(yùn)行了。。。");
this.setState(RUNNING_STATE);//現(xiàn)在是運(yùn)行狀態(tài)
break;
case RUNNING_STATE:
//do nothing 已經(jīng)是運(yùn)行狀態(tài)了
break;
case STOPPING_STATE:
System.out.println("電梯開(kāi)始運(yùn)行了。。。");
this.setState(RUNNING_STATE);
break;
}
}
//執(zhí)行停止動(dòng)作
@Override
public void stop() {
switch (this.state) {
case OPENING_STATE: //開(kāi)門(mén)的電梯已經(jīng)是是停止的了(正常情況下)
//do nothing
break;
case CLOSING_STATE://關(guān)門(mén)時(shí)才可以停止
System.out.println("電梯停止了。。。");
this.setState(STOPPING_STATE);
break;
case RUNNING_STATE://運(yùn)行時(shí)當(dāng)然可以停止了
System.out.println("電梯停止了。。。");
this.setState(STOPPING_STATE);
break;
case STOPPING_STATE:
//do nothing
break;
}
}
}
public class Client {
public static void main(String[] args) {
Lift lift = new Lift();
lift.setState(ILift.STOPPING_STATE);//電梯是停止的
lift.open();//開(kāi)門(mén)
lift.close();//關(guān)門(mén)
lift.run();//運(yùn)行
lift.stop();//停止
}
}
問(wèn)題分析:
- 使用了大量的switch…case這樣的判斷(if…else也是一樣),使程序的可閱讀性變差。
- 擴(kuò)展性很差。如果新加了斷電的狀態(tài),我們需要修改上面判斷邏輯
定義:
對(duì)有狀態(tài)的對(duì)象,把復(fù)雜的“判斷邏輯”提取到不同的狀態(tài)對(duì)象中,允許狀態(tài)對(duì)象在其內(nèi)部狀態(tài)發(fā)生改變時(shí)改變其行為。
2.結(jié)構(gòu)
狀態(tài)模式包含以下主要角色。
- 環(huán)境(Context)角色:也稱為上下文,它定義了客戶程序需要的接口,維護(hù)一個(gè)當(dāng)前狀態(tài),并將與狀態(tài)相關(guān)的操作委托給當(dāng)前狀態(tài)對(duì)象來(lái)處理。
- 抽象狀態(tài)(State)角色:定義一個(gè)接口,用以封裝環(huán)境對(duì)象中的特定狀態(tài)所對(duì)應(yīng)的行為。
- 具體狀態(tài)(Concrete State)角色:實(shí)現(xiàn)抽象狀態(tài)所對(duì)應(yīng)的行為。
3.案例實(shí)現(xiàn)
/**
* @author 曉風(fēng)殘?jiān)翷x
* @date 2023/7/29 14:57
* 抽象狀態(tài)類(lèi)
*/
public abstract class LiftState {
// 聲明環(huán)境角色類(lèi)變量
protected Context context;
public void setContext(Context context) {
this.context = context;
}
public Context getContext() {
return context;
}
// 電梯開(kāi)啟操作
public abstract void open();
// 電梯關(guān)閉操作
public abstract void close();
// 電梯運(yùn)行操作
public abstract void run();
// 電梯停止操作
public abstract void stop();
}
/**
* @author 曉風(fēng)殘?jiān)翷x
* @date 2023/7/29 15:00
* 電梯開(kāi)啟狀態(tài)類(lèi)
*/
public class OpeningState extends LiftState {
//開(kāi)啟當(dāng)然可以關(guān)閉了,我就想測(cè)試一下電梯門(mén)開(kāi)關(guān)功能
@Override
public void open() {
System.out.println("電梯門(mén)開(kāi)啟...");
}
@Override
public void close() {
//狀態(tài)修改
super.context.setLiftState(Context.CLOSING_STATE);
//動(dòng)作委托為CloseState來(lái)執(zhí)行,也就是委托給了ClosingState子類(lèi)執(zhí)行這個(gè)動(dòng)作
super.context.getLiftState().close();
}
//電梯門(mén)不能開(kāi)著就跑,這里什么也不做
@Override
public void run() {
//do nothing
}
//開(kāi)門(mén)狀態(tài)已經(jīng)是停止的了
@Override
public void stop() {
//do nothing
}
}
/**
* @author 曉風(fēng)殘?jiān)翷x
* @date 2023/7/29 15:00
* 電梯運(yùn)行狀態(tài)類(lèi)
*/
public class RunningState extends LiftState {
//運(yùn)行的時(shí)候開(kāi)電梯門(mén)?你瘋了!電梯不會(huì)給你開(kāi)的
@Override
public void open() {
//do nothing
}
//電梯門(mén)關(guān)閉?這是肯定了
@Override
public void close() {//雖然可以關(guān)門(mén),但這個(gè)動(dòng)作不歸我執(zhí)行
//do nothing
}
//這是在運(yùn)行狀態(tài)下要實(shí)現(xiàn)的方法
@Override
public void run() {
System.out.println("電梯正在運(yùn)行...");
}
//這個(gè)事絕對(duì)是合理的,光運(yùn)行不停止還有誰(shuí)敢做這個(gè)電梯?!估計(jì)只有上帝了
@Override
public void stop() {
super.context.setLiftState(Context.STOPPING_STATE);
super.context.stop();
}
}
/**
* @author 曉風(fēng)殘?jiān)翷x
* @date 2023/7/29 15:00
* 電梯停止?fàn)顟B(tài)類(lèi)
*/
public class StoppingState extends LiftState {
//停止?fàn)顟B(tài),開(kāi)門(mén),那是要的!
@Override
public void open() {
//狀態(tài)修改
super.context.setLiftState(Context.OPENING_STATE);
//動(dòng)作委托為CloseState來(lái)執(zhí)行,也就是委托給了ClosingState子類(lèi)執(zhí)行這個(gè)動(dòng)作
super.context.getLiftState().open();
}
@Override
public void close() {//雖然可以關(guān)門(mén),但這個(gè)動(dòng)作不歸我執(zhí)行
//狀態(tài)修改
super.context.setLiftState(Context.CLOSING_STATE);
//動(dòng)作委托為CloseState來(lái)執(zhí)行,也就是委托給了ClosingState子類(lèi)執(zhí)行這個(gè)動(dòng)作
super.context.getLiftState().close();
}
//停止?fàn)顟B(tài)再跑起來(lái),正常的很
@Override
public void run() {
//狀態(tài)修改
super.context.setLiftState(Context.RUNNING_STATE);
//動(dòng)作委托為CloseState來(lái)執(zhí)行,也就是委托給了ClosingState子類(lèi)執(zhí)行這個(gè)動(dòng)作
super.context.getLiftState().run();
}
//停止?fàn)顟B(tài)是怎么發(fā)生的呢?當(dāng)然是停止方法執(zhí)行了
@Override
public void stop() {
System.out.println("電梯停止了...");
}
}
/**
* @author 曉風(fēng)殘?jiān)翷x
* @date 2023/7/29 15:00
* 電梯關(guān)閉狀態(tài)類(lèi)
*/
public class ClosingState extends LiftState {
@Override
//電梯門(mén)關(guān)閉,這是關(guān)閉狀態(tài)要實(shí)現(xiàn)的動(dòng)作
public void close() {
System.out.println("電梯門(mén)關(guān)閉...");
}
//電梯門(mén)關(guān)了再打開(kāi),逗你玩呢,那這個(gè)允許呀
@Override
public void open() {
super.context.setLiftState(Context.OPENING_STATE);
super.context.open();
}
//電梯門(mén)關(guān)了就跑,這是再正常不過(guò)了
@Override
public void run() {
super.context.setLiftState(Context.RUNNING_STATE);
super.context.run();
}
//電梯門(mén)關(guān)著,我就不按樓層
@Override
public void stop() {
super.context.setLiftState(Context.STOPPING_STATE);
super.context.stop();
}
}
/**
* @author 曉風(fēng)殘?jiān)翷x
* @date 2023/7/29 14:58
* 環(huán)境角色類(lèi)
*/
public class Context {
// 定義對(duì)應(yīng)狀態(tài)對(duì)象的常量
public final static OpeningState OPENING_STATE = new OpeningState();
public final static RunningState RUNNING_STATE = new RunningState();
public final static ClosingState CLOSING_STATE = new ClosingState();
public final static StoppingState STOPPING_STATE = new StoppingState();
// 定義一個(gè)當(dāng)前電梯狀態(tài)變量
private LiftState liftState;
// 設(shè)置當(dāng)前狀態(tài)對(duì)象
public void setLiftState(LiftState liftState) {
this.liftState = liftState;
// 設(shè)置當(dāng)前狀態(tài)對(duì)象中的Context對(duì)象
this.liftState.setContext(this);
}
public LiftState getLiftState() {
return liftState;
}
public void open() {
this.liftState.open();
}
public void close() {
this.liftState.close();
}
public void stop() {
this.liftState.stop();
}
public void run() {
this.liftState.run();
}
}
//測(cè)試類(lèi)
public class Client {
public static void main(String[] args) {
Context context = new Context();
context.setLiftState(new RunningState());
context.open();
context.close();
context.run();
context.stop();
}
}
4.優(yōu)缺點(diǎn)
1.優(yōu)點(diǎn):文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-732157.html
- 將所有與某個(gè)狀態(tài)有關(guān)的行為放到一個(gè)類(lèi)中,并且可以方便地增加新的狀態(tài),只需要改變對(duì)象狀態(tài)即可改變對(duì)象的行為。
- 允許狀態(tài)轉(zhuǎn)換邏輯與狀態(tài)對(duì)象合成一體,而不是某一個(gè)巨大的條件語(yǔ)句塊。
2.缺點(diǎn):文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-732157.html
- 狀態(tài)模式的使用必然會(huì)增加系統(tǒng)類(lèi)和對(duì)象的個(gè)數(shù)。
- 狀態(tài)模式的結(jié)構(gòu)與實(shí)現(xiàn)都較為復(fù)雜,如果使用不當(dāng)將導(dǎo)致程序結(jié)構(gòu)和代碼的混亂。
- 狀態(tài)模式對(duì)"開(kāi)閉原則"的支持并不太好。
5.使用場(chǎng)景
- 當(dāng)一個(gè)對(duì)象的行為取決于它的狀態(tài),并且它必須在運(yùn)行時(shí)根據(jù)狀態(tài)改變它的行為時(shí),就可以考慮使用狀態(tài)模式。
- 一個(gè)操作中含有龐大的分支結(jié)構(gòu),并且這些分支決定于對(duì)象的狀態(tài)時(shí)。
到了這里,關(guān)于Gof23設(shè)計(jì)模式之狀態(tài)模式的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!