策略模式和狀態(tài)模式都比較簡(jiǎn)單好理解,都是為了解決一個(gè)問題有多種方案這樣的場(chǎng)景,兩者結(jié)構(gòu)幾乎一樣。
不同的是狀態(tài)模式的行為是平行的,不可替換的,一種狀態(tài)就明確對(duì)應(yīng)一種行為。
而策略模式的行為是彼此獨(dú)立、可相互替換的。一種是抽象狀態(tài),一種是抽象策略。
一、策略模式
1. 定義
策略模式定義了一系列的算法,并將每一個(gè)算法封裝起來,使它們可以相互替換。策略模式讓算法獨(dú)立于使用它的客戶而獨(dú)立變化。
如果將所有算法都放在一個(gè)類中,使用if-else或者switch來判斷使用哪一個(gè),就會(huì)使程序變得非常繁雜,并且這個(gè)類使用了多種計(jì)算方式違背了單一指責(zé)原則。如果想要增加一個(gè)算法,就必須修改原先封裝好的代碼,可擴(kuò)展性差,這就出現(xiàn)了策略模式
2. 應(yīng)用場(chǎng)景
- 針對(duì)同一類型問題的多種處理方式,僅僅是具體行為有差別時(shí)。
- 需要安全地封裝多種同一類型的操作。
3. 實(shí)現(xiàn)
UML

抽象策略類
public interface CalculateStrategy {
int calculatePrice(int km);
}
具體策略類
public class BusStrategy implements CalculateStrategy {
@Override
public int calculatePrice(int km) {
int price = 0;
// 具體算法
return price;
}
}
public class SubwayStrategy implements CalculateStrategy {
@Override
public int calculatePrice(int km) {
int price = 0;
// 具體算法
return price;
}
}
使用
public class TrafficCalculator {
public static void main(String[] args) {
TrafficCalculator calculator = new TrafficCalculator();
calculator.setStrategy(new CarStrategy());
calculator.calculatePrice(55);
}
CalculateStrategy mStrategy;
public void setStrategy(CalculateStrategy strategy) {
mStrategy = strategy;
}
public int calculatePrice(int km) {
return mStrategy.calculatePrice(km);
}
}
4. 小結(jié)
策略模式主要用來分離算法,在相同的行為抽象下有不同的具體實(shí)現(xiàn)策略。這個(gè)模式很好地演示了開閉原則,也就是定義抽象,注入不同的實(shí)現(xiàn),從而達(dá)到很好的可擴(kuò)展性。
優(yōu)點(diǎn)
- 結(jié)構(gòu)清晰、使用簡(jiǎn)單直觀。
- 耦合度相對(duì)而言較低,擴(kuò)展方便。
- 操作封裝徹底,數(shù)據(jù)更為安全。
缺點(diǎn)
- 隨著策略的增加,子類會(huì)變得繁多。
二、狀態(tài)模式
1. 定義
當(dāng)一個(gè)對(duì)象的內(nèi)在狀態(tài)改變時(shí)允許改變其行為,這個(gè)對(duì)象看起來像是改變了其類。
2. 使用場(chǎng)景
- 一個(gè)對(duì)象的行為取決于它的狀態(tài),必須在運(yùn)行時(shí)由狀態(tài)覺得行為。
- 代碼中包含大量與狀態(tài)有關(guān)的條件語句。
3. 實(shí)現(xiàn)
UML

狀態(tài)抽象類
public interface TvState {
void nextChannel();
void prevChannel();
void turnUp();
void turnDown();
}
具體狀態(tài)類
關(guān)機(jī)狀態(tài)下什么都做不了。
public class PowerOffState implements TvState {
@Override
public void nextChannel() { }
@Override
public void prevChannel() { }
@Override
public void turnUp() { }
@Override
public void turnDown() { }
}
public class PowerOnState implements TvState {
@Override
public void nextChannel() {
System.out.println("下一個(gè)");
}
@Override
public void prevChannel() {
System.out.println("上一個(gè)");
}
@Override
public void turnUp() {
System.out.println("調(diào)大");
}
@Override
public void turnDown() {
System.out.println("調(diào)小");
}
}
電源
public interface PowerController {
void powerOn();
void powerOff();
}
控制
封裝了狀態(tài)決定行為的實(shí)現(xiàn)。
public class TvController implements PowerController {
private TvState mTvState;
public void setTvState(TvState tvState) {
mTvState = tvState;
}
@Override
public void powerOn() {
setTvState(new PowerOnState());
}
@Override
public void powerOff() {
setTvState(new PowerOffState());
}
public void nextChannel() {
mTvState.nextChannel();
}
public void prevChannel() {
mTvState.prevChannel();
}
public void turnUp() {
mTvState.turnUp();
}
public void turnDown() {
mTvState.turnDown();
}
}
調(diào)用代碼
public class Test {
public static void test() {
TvController controller = new TvController();
controller.powerOn();
controller.nextChannel();
controller.powerOff();
controller.turnUp();
}
}
4. 小結(jié)
狀態(tài)模式應(yīng)用還是很廣泛的,在最常見的登錄模塊就可以使用,去避免重復(fù)判斷登錄狀態(tài)和未登錄狀態(tài)。使用狀態(tài)模式可以消除重復(fù)的if-else邏輯、結(jié)構(gòu)更為清晰,也使這個(gè)模塊的可擴(kuò)展性和靈活性更高。
優(yōu)點(diǎn)
- 將與一個(gè)特定狀態(tài)相關(guān)的行為都放入一個(gè)狀態(tài)對(duì)象中,更好地組織與特定狀態(tài)的行為相關(guān)的代碼。
- 繁瑣的狀態(tài)判斷轉(zhuǎn)換成狀態(tài)類族,避免代碼膨脹,保證可擴(kuò)展性和可維護(hù)性。
缺點(diǎn)
- 會(huì)增加系統(tǒng)類和對(duì)象的個(gè)數(shù)。