?某信用卡業(yè)務(wù)系統(tǒng),銀行賬戶存在3種狀態(tài),且在不同狀態(tài)下存在不同的行為:
1)正常狀態(tài)(余額大等于0),用戶可以存款也可以取款;
2)透支狀態(tài)(余額小于0且大于-2000),用戶可以存款也可以取款,但需要對欠款支付利息。
3)受限狀態(tài)(余額小等于-2000),用戶只能存款,還需要對欠款支付利息。
圖 偽代碼實(shí)現(xiàn)上述需求
上面代碼存在以下問題:
1)獲取狀態(tài)時,有好多個if分支,如果再增加幾個狀態(tài),則需要增加判斷條件,同時也不符合開閉原則。
2)在進(jìn)行存取款操作時,有對狀態(tài)進(jìn)行判斷的條件,行為受到狀態(tài)的限制。
為了更好對具有多種狀態(tài)的對象進(jìn)行設(shè)計(jì),可以使用一種被稱作狀態(tài)模式的設(shè)計(jì)模式。
1 狀態(tài)模式
狀態(tài)模式(State Pattern)允許一個對象在其內(nèi)部狀態(tài)改變時改變它的行為,對象看起來似乎修改了它的狀態(tài)類。是一種對象行為型模式。
圖 狀態(tài)模式UML
Context:環(huán)境類,是擁有多種狀態(tài)的對象。由于環(huán)境類的狀態(tài)存在多樣性且在不同狀態(tài)下對象的行為有所不同,因此將狀態(tài)獨(dú)立出去形成單獨(dú)的狀態(tài)類。
State:抽象狀態(tài)類,用于定義一個接口以封裝與環(huán)境類的一個特定狀態(tài)相關(guān)的行為。在抽象狀態(tài)類中聲明各種不同狀態(tài)對應(yīng)的方法,而在其子類中實(shí)現(xiàn)這些方法。
ConcreteState:具體狀態(tài)類,是抽象狀態(tài)類的子類,每個子類實(shí)現(xiàn)與環(huán)境類的一個狀態(tài)相關(guān)的行為。
public class UserAccount {
private double balance;
private CardState cardState;
public UserAccount(double balance) {
this.balance = balance;
cardState = new NormalCardState(this);
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
public void deposit(double money) {
System.out.println("存錢 " + money);
cardState.deposit(money);
changeState();
System.out.println("信用卡余額:"+ balance + ",狀態(tài)是:" + cardState.getState());
System.out.println("------------------------------");
}
public void withdraw(double money) {
if (balance - money < 0) {
System.out.println("借錢 " + money + ",利息利率是0.01");
} else {
System.out.println("取款 " + money);
}
cardState.withdraw(money);
changeState();
System.out.println("信用卡余額:"+ balance + ",狀態(tài)是:" + cardState.getState());
System.out.println("------------------------------");
}
public void changeState() {
if (balance > 0) {
if (!"正常".equals(cardState.getState())) cardState = new NormalCardState(this);
} else if (balance > -2000) {
if (!"透支".equals(cardState.getState())) cardState = new OverdraftCardState(this);
} else {
if (!"受限".equals(cardState.getState())) cardState = new LimitationCardState(this);
}
}
public void setCardState(CardState cardState) {
this.cardState = cardState;
}
}
public abstract class CardState {
protected final UserAccount userAccount;
public CardState(UserAccount userAccount) {
this.userAccount = userAccount;
}
public abstract void deposit(double money); // 存款
public abstract void withdraw(double money); // 取款
public abstract void payInterest(); // 支付利息
public abstract String getState(); // 獲取狀態(tài)
}
public class BankService {
public static void main(String[] args) {
// 開戶
UserAccount userAccount = new UserAccount(1000);
userAccount.withdraw(500);
userAccount.deposit(200);
userAccount.withdraw(1000);
userAccount.deposit(100);
userAccount.withdraw(2000);
userAccount.withdraw(500);
}
}
//取款 500.0
//信用卡余額:500.0,狀態(tài)是:正常
//------------------------------
//存錢 200.0
//信用卡余額:700.0,狀態(tài)是:正常
//------------------------------
//借錢 1000.0,利息利率是0.01
//信用卡余額:-300.0,狀態(tài)是:透支
//------------------------------
//存錢 100.0
//支付利息:-3.0
//信用卡余額:-203.0,狀態(tài)是:透支
//------------------------------
//借錢 2000.0,利息利率是0.01
//支付利息:-2.0300000000000002
//信用卡余額:-2205.03,狀態(tài)是:受限
//------------------------------
//借錢 500.0,利息利率是0.01
//該賬戶已受限,不能取款
//信用卡余額:-2205.03,狀態(tài)是:受限
//------------------------------
public class NormalCardState extends CardState{
public NormalCardState(UserAccount userAccount) {
super(userAccount);
}
@Override
public void deposit(double money) {
userAccount.setBalance(userAccount.getBalance() + money);
}
@Override
public void withdraw(double money) {
userAccount.setBalance(userAccount.getBalance() - money);
}
@Override
public void payInterest() {
}
@Override
public String getState() {
return "正常";
}
}
public class OverdraftCardState extends CardState{
public OverdraftCardState(UserAccount userAccount) {
super(userAccount);
}
@Override
public void deposit(double money) {
payInterest();
userAccount.setBalance(userAccount.getBalance() + money);
}
@Override
public void withdraw(double money) {
payInterest();
userAccount.setBalance(userAccount.getBalance() - money);
}
@Override
public void payInterest() {
System.out.println("支付利息:" + userAccount.getBalance() * 0.01);
userAccount.setBalance(userAccount.getBalance() * ( 1 + 0.01));
}
@Override
public String getState() {
return "透支";
}
}
public class LimitationCardState extends CardState{
public LimitationCardState(UserAccount userAccount) {
super(userAccount);
}
@Override
public void deposit(double money) {
payInterest();
userAccount.setBalance(userAccount.getBalance() + money);
}
@Override
public void withdraw(double money) {
System.out.println("該賬戶已受限,不能取款");
}
@Override
public void payInterest() {
System.out.println("支付利息:" + userAccount.getBalance() * 0.01);
userAccount.setBalance(userAccount.getBalance() * ( 1 + 0.01));
}
@Override
public String getState() {
return "受限";
}
}
使用狀態(tài)模式后,在編碼過程中,可以不要在關(guān)系具體狀態(tài),只需專注實(shí)現(xiàn)具體狀態(tài)下的業(yè)務(wù)。
1.1 狀態(tài)轉(zhuǎn)換方式
在狀態(tài)模式中,環(huán)境類的狀態(tài)轉(zhuǎn)換方式有兩種:
1)在環(huán)境類完成轉(zhuǎn)換。(上面代碼是以這種形式)
2)在具體狀態(tài)類中完成轉(zhuǎn)換。
圖 兩種狀態(tài)轉(zhuǎn)換方式的比較
如果新增狀態(tài)類,則兩種方式都需要在各自的類中做修改。都不符合開閉原則。
1.2 共享狀態(tài)
在有些情況下,多個環(huán)境類對象需要共享一個狀態(tài),這時需要把狀態(tài)對象定義為一個靜態(tài)成員對象。
需求:一個房間有兩個開關(guān)來控制燈泡的開關(guān)。開關(guān)等功能是固定的(打開只能使燈泡亮起,關(guān)閉只能使燈泡熄滅。
public class LightSwitch {
private final static LightState onState = new OnLightState(),offState = new OffLightState();
private static LightState lightState = offState;
private final String name;
public LightSwitch(String name) {
this.name = name;
}
public static void changeLightState(String type) {
if ("on".equalsIgnoreCase(type)) {
lightState = onState;
} else {
lightState = offState;
}
}
public void off() {
System.out.println(name + "關(guān)閉操作");
lightState.off(this);
}
public void on() {
System.out.println(name + "打開操作");
lightState.on(this);
}
}
public abstract class LightState {
public abstract void on(LightSwitch lightSwitch);
public abstract void off(LightSwitch lightSwitch);
}
public class OnLightState extends LightState{
@Override
public void on(LightSwitch lightSwitch) {
System.out.println("燈泡已打開");
System.out.println("--------------");
}
@Override
public void off(LightSwitch lightSwitch) {
System.out.println("關(guān)閉成功");
LightSwitch.changeLightState("off");
System.out.println("--------------");
}
}
public class OffLightState extends LightState{
@Override
public void on(LightSwitch lightSwitch) {
System.out.println("打開成功");
LightSwitch.changeLightState("on");
System.out.println("--------------");
}
@Override
public void off(LightSwitch lightSwitch) {
System.out.println("燈泡已關(guān)閉");
System.out.println("--------------");
}
}
public class PeopleOpera {
public static void main(String[] args) {
LightSwitch lightSwitch1 = new LightSwitch("開關(guān)1");
LightSwitch lightSwitch2 = new LightSwitch("開關(guān)2");
lightSwitch1.on();
lightSwitch2.off();
lightSwitch1.off();
lightSwitch1.on();
lightSwitch2.on();
lightSwitch2.off();
}
}
//開關(guān)1打開操作
//打開成功
//--------------
//開關(guān)2關(guān)閉操作
//關(guān)閉成功
//--------------
//開關(guān)1關(guān)閉操作
//燈泡已關(guān)閉
//--------------
//開關(guān)1打開操作
//打開成功
//--------------
//開關(guān)2打開操作
//燈泡已打開
//--------------
//開關(guān)2關(guān)閉操作
//關(guān)閉成功
//--------------
2 優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
1)環(huán)境類轉(zhuǎn)換狀態(tài)方式,封裝狀態(tài)的轉(zhuǎn)換規(guī)則,對狀態(tài)轉(zhuǎn)換代碼集中管理。
2)將所有與具體狀態(tài)有關(guān)的行為都封裝在一個類中。
3)可以讓多個環(huán)境對象共享一個狀態(tài)對象,從而減少系統(tǒng)中對象個數(shù)。
4)在具體狀態(tài)類中轉(zhuǎn)換狀態(tài)方式,將狀態(tài)轉(zhuǎn)換邏輯與狀態(tài)對象合成一起,避免使用龐大的條件語句塊來將業(yè)務(wù)方法和狀態(tài)轉(zhuǎn)換代碼交織在一起。
缺點(diǎn):
1)增加了類和對象的個數(shù)。
2)實(shí)現(xiàn)較為復(fù)雜,增加了系統(tǒng)設(shè)計(jì)難度。
3)對開閉原則的支持并不好。
3 適用場景
1)對象的行為依賴它的狀態(tài),且狀態(tài)之間互相轉(zhuǎn)換。文章來源:http://www.zghlxwxcb.cn/news/detail-715948.html
2)代碼中包含大量與對象狀態(tài)有關(guān)的條件語句。文章來源地址http://www.zghlxwxcb.cn/news/detail-715948.html
到了這里,關(guān)于狀態(tài)模式-對象狀態(tài)及其轉(zhuǎn)換的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!