1、定義、使用場(chǎng)景
定義:在不同的狀態(tài)下,對(duì)同一行為有不同的響應(yīng)。狀態(tài)模式把對(duì)象的行為包裝在不同的狀態(tài)中,每一個(gè)狀態(tài)的對(duì)象都有一個(gè)相同的抽象狀態(tài)基類,并實(shí)現(xiàn)基類對(duì)應(yīng)的方法。這樣當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生改變時(shí),其行為也會(huì)隨之改變。
使用場(chǎng)景:當(dāng)一個(gè)對(duì)象的行為受其對(duì)應(yīng)的狀態(tài)的影響時(shí)。例如:手機(jī)的飛行模式打開和關(guān)閉是兩個(gè)狀態(tài),在關(guān)閉飛行模式時(shí)可以打電話、發(fā)短信,打開飛行模式時(shí),雖然打電話和發(fā)短信的功能存在,但卻無法正常使用。
2、角色
State:抽象狀態(tài)類或者接口,其中的方法表示對(duì)應(yīng)狀態(tài)下的行為。
StateA、StateB:State的具體實(shí)現(xiàn)類,以實(shí)現(xiàn)對(duì)應(yīng)狀態(tài)下具體的行為。
Context:維護(hù)當(dāng)前對(duì)象所對(duì)應(yīng)的狀態(tài)。
3、實(shí)現(xiàn)
App中登錄操作是常見的功能,例如在使用京東、或者淘寶購物時(shí),在用戶未登錄的狀態(tài)下只能進(jìn)行商品的瀏覽,如果點(diǎn)擊購買按鈕,則會(huì)跳轉(zhuǎn)到登錄界面,用戶登陸后則可進(jìn)行商品購買操作。可見在登錄與否的狀態(tài)下,對(duì)于購買的操作有著不同的響應(yīng)處理。接下來我們來模擬這個(gè)簡單的過程。
首先我們定義一個(gè)State接口,里邊包含一個(gè)購買的方法。
public interface UserState {
void buy(Context context);
}
然后在登錄狀態(tài)與未登錄狀態(tài)下分別實(shí)現(xiàn)這個(gè)接口:
public class LoginState implements UserState {
@Override
public void buy(Context context) {
Toast.makeText(context, "購買成功!", Toast.LENGTH_SHORT).show();
}
}
public class LogoutState implements UserState {
@Override
public void buy(Context context) {
context.startActivity(new Intent(context, LoginActivity.class));
}
}
在登錄狀態(tài)下,則直接進(jìn)行購買操作,通過Toast提示購買成功。在未登錄狀態(tài)下則跳轉(zhuǎn)到登錄界面。
接下來定義一個(gè)LoginContext類,來維護(hù)用戶當(dāng)前的登錄狀態(tài):
public class LoginContext {
//默認(rèn)未登錄
private UserState currentState = new LogoutState();
private LoginContext() {
}
//通過單例模式獲得LoginContext的對(duì)象
public static LoginContext getInstance() {
return SingletonHolder.instance;
}
private static class SingletonHolder {
private static final LoginContext instance = new LoginContext();
}
//設(shè)置登錄狀態(tài)
public void setState(UserState state) {
currentState = state;
}
//購買操作
public void buy(Context context) {
currentState.buy(context);
}
}
可以看到,通過setState()方法來更改用戶登錄狀態(tài),在不同的狀態(tài)下 currentState.buy(context)操作有著不同的響應(yīng):購買成功或者登錄跳轉(zhuǎn)。即不同的狀態(tài)對(duì)象可對(duì)同一個(gè)操作進(jìn)行不同的處理。
我們創(chuàng)建一個(gè)MainActiciy,里邊包含購買和退出登錄兩個(gè)操作:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.buy).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
LoginContext.getInstance().buy(MainActivity.this);
}
});
findViewById(R.id.logout).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
LoginContext.getInstance().setState(new LogoutState());
Toast.makeText(MainActivity.this, "已退出登錄", Toast.LENGTH_SHORT).show();
}
});
}
}
同時(shí)還需要一個(gè)LoginActivity進(jìn)行登錄操作:
public class LoginActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
findViewById(R.id.login).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
LoginContext.getInstance().setState(new LoginState());
Toast.makeText(LoginActivity.this, "登錄成功", Toast.LENGTH_SHORT).show();
finish();
}
});
}
}
運(yùn)行后如下:
由于LoginContext中的默認(rèn)狀態(tài)為未登錄,則點(diǎn)擊購買按鈕會(huì)跳轉(zhuǎn)到登錄界面:
點(diǎn)擊登錄按鈕后,則在LoginContext中會(huì)將當(dāng)前用戶狀態(tài)修改為已登錄,同時(shí)返回主頁,此時(shí)點(diǎn)擊購買按鈕則會(huì)提示購買成功:
再點(diǎn)擊退出登錄,則在LoginContext中又會(huì)將當(dāng)前用戶狀態(tài)修改為未登錄。之后再購買的話有需要先進(jìn)行登錄操作。
4、小結(jié)
如果不采用狀態(tài)模式,則在所有購買操作的地方都要先進(jìn)行登錄檢測(cè),這樣可能會(huì)出現(xiàn)大量的if-else邏輯,如果繼續(xù)增加用戶的狀態(tài),則邏輯判斷會(huì)更加的復(fù)雜,增加代碼的維護(hù)難度,一不小心可能會(huì)出現(xiàn)權(quán)限的問題。通過狀態(tài)模式,我們將具體的行為封裝到狀態(tài)類中,省去了if-else判斷,如果要增加狀態(tài)則只需要添加新的State實(shí)現(xiàn)類即可,不用去修改具體的業(yè)務(wù)邏輯,更加的靈活、方便擴(kuò)展。