如果物件内的方法,会依据物件内的状态,使用多个 if - else if - else
或 switch case
来判定不同状态下该执行的内容时,可以将 switch case
的逻辑抽取出多个独立的物件,负责状态的变化以及执行的内容。
通常发生在物件内的方法会依据自身的状态,产生 if - else if - else
或 switch case
的逻辑判断,如果判断的情境数量太多会丧失程序的弹性,导致之後不易於维护以及修改。因此可以将所有的情境独自转换成单一物件,每个物件将执行各自的任务,而且还能够在需要时更改物件的状态,让下次物件执行相同方法时,会自动执行新状态下的相关程序码。
作法是:
Context
)。State
物件),除了将原本逻辑判断内实作细节的程序码搬移过来,还可以依据需求,更改 Context
的状态。Context
:
Context
的状态更改为 State
物件。State
的方法。以下范例以「简易型电视游乐器」为核心制作。
制作独立物件的虚拟层亲代:State
public interface State {
public abstract void tryToShot();
public abstract void tryToKick();
public abstract void tryToJump();
public abstract void tryToMovingToRight();
public abstract void tryToMovingToLeft();
public abstract void tryToDoSomething();
public abstract void tryToBecomeSnipperMode();
public abstract void tryToBecomeStandingMode();
public abstract void tryToBecomeBoostMode();
}
制作具有多个逻辑判断的物件:Hero
(Context 物件)
public class Hero {
private State state;
public Hero() {
this.state = new StandingState(this);
}
public void changeState(State state) {
this.state = state;
}
public State getState() {
return state;
}
public void tryToShot() {
state.tryToShot();
}
public void shot() {
System.out.println("英雄射出一发子弹");
}
public void tryToKick() {
state.tryToKick();
}
public void kick() {
System.out.println("英雄向前方踢了一脚");
}
public void tryToJump() {
state.tryToJump();
}
public void jump() {
System.out.println("英雄往上跳");
}
public void tryToMovingToRight() {
state.tryToMovingToRight();
}
public void moveToRight() {
System.out.println("英雄往右走");
}
public void tryToMovingToLeft() {
state.tryToMovingToLeft();
}
public void moveToLeft() {
System.out.println("英雄往左走");
}
public void tryToDoSomething() {
state.tryToDoSomething();
}
public void doNothing() {
System.out.println("什麽事也没发生");
}
public void tryToBecomeSnipperMode() {
state.tryToBecomeSnipperMode();
}
public void tryToBecomeStandingMode() {
state.tryToBecomeStandingMode();
}
public void tryToBecomeBoostMode() {
state.tryToBecomeBoostMode();
}
}
制作独立物件的子代:StandingState
、SnipperState
、BoostState
(State 物件)
public class StandingState implements State {
private Hero hero;
public StandingState(Hero hero) {
this.hero = hero;
}
@Override
public void tryToShot() {
hero.shot();
}
@Override
public void tryToKick() {
hero.kick();
}
@Override
public void tryToJump() {
hero.jump();
}
@Override
public void tryToMovingToRight() {
hero.moveToRight();
}
@Override
public void tryToMovingToLeft() {
hero.moveToLeft();
}
@Override
public void tryToDoSomething() {
hero.doNothing();
}
@Override
public void tryToBecomeSnipperMode() {
hero.changeState(new SnipperState(hero));
}
@Override
public void tryToBecomeStandingMode() {
hero.changeState(new StandingState(hero));
}
@Override
public void tryToBecomeBoostMode() {
hero.changeState(new BoostState(hero));
}
}
public class SnipperState implements State {
private Hero hero;
public SnipperState(Hero hero) {
this.hero = hero;
}
@Override
public void tryToShot() {
hero.shot();
}
@Override
public void tryToKick() {
hero.doNothing();
}
@Override
public void tryToJump() {
hero.doNothing();
}
@Override
public void tryToMovingToRight() {
hero.doNothing();
}
@Override
public void tryToMovingToLeft() {
hero.doNothing();
}
@Override
public void tryToDoSomething() {
hero.doNothing();
}
@Override
public void tryToBecomeSnipperMode() {
hero.changeState(new SnipperState(hero));
}
@Override
public void tryToBecomeStandingMode() {
hero.changeState(new StandingState(hero));
}
@Override
public void tryToBecomeBoostMode() {
hero.changeState(new BoostState(hero));
}
}
public class BoostState implements State {
private Hero hero;
public BoostState(Hero hero) {
this.hero = hero;
}
@Override
public void tryToShot() {
hero.doNothing();
}
@Override
public void tryToKick() {
hero.doNothing();
}
@Override
public void tryToJump() {
hero.jump();
}
@Override
public void tryToMovingToRight() {
hero.moveToRight();
}
@Override
public void tryToMovingToLeft() {
hero.moveToLeft();
}
@Override
public void tryToDoSomething() {
hero.doNothing();
}
@Override
public void tryToBecomeSnipperMode() {
hero.changeState(new BoostState(hero));
}
@Override
public void tryToBecomeStandingMode() {
hero.changeState(new StandingState(hero));
}
@Override
public void tryToBecomeBoostMode() {
hero.changeState(new BoostState(hero));
}
}
制作掌机的摇杆:VideoGameController
public class VideoGameController {
private Hero hero;
public VideoGameController(Hero hero) {
this.hero = hero;
}
public void pushButtonY() {
hero.tryToShot();
}
public void pushButtonX() {
hero.tryToKick();
}
public void pushButtonB() {
hero.tryToJump();
}
public void pushButtonA() {
hero.tryToDoSomething();
}
public void pushButtonRightArrow() {
hero.tryToMovingToRight();
}
public void pushButtonLeftArrow() {
hero.tryToMovingToLeft();
}
public void holdButtonR() {
hero.tryToBecomeSnipperMode();
}
public void releaseButtonR() {
hero.tryToBecomeStandingMode();
}
public void holdButtonL() {
hero.tryToBecomeBoostMode();
}
public void releaseButtonL() {
hero.tryToBecomeStandingMode();
}
}
测试,模拟玩家不断在不同模式间尝试按钮:VideoGameStateSample
public class VideoGameStateSample {
public static void main(String[] args) {
Hero hero = new Hero();
VideoGameController controller = new VideoGameController(hero);
System.out.println("---尝试基本按钮---");
standardOperation(controller);
System.out.println("\n---按住按钮 R---");
controller.holdButtonR();
standardOperation(controller);
System.out.println("\n---放开按钮 R---");
controller.releaseButtonR();
standardOperation(controller);
System.out.println("\n---按住按钮 L---");
controller.holdButtonL();
standardOperation(controller);
System.out.println("\n---放开按钮 L---");
controller.releaseButtonL();
standardOperation(controller);
}
private static void standardOperation(VideoGameController controller) {
controller.pushButtonY();
controller.pushButtonX();
controller.pushButtonB();
controller.pushButtonA();
controller.pushButtonRightArrow();
controller.pushButtonLeftArrow();
}
}
制作独立物件的虚拟层亲代:State
/** @abstract */
class State {
/** @param {Hero} hero */
constructor(hero) {
this.hero = hero;
}
tryToShot() { return; }
tryToKick() { return; }
tryToJump() { return; }
tryToMovingToRight() { return; }
tryToMovingToLeft() { return; }
tryToDoSomething() { return; }
tryToBecomeSnipperMode() { return; }
tryToBecomeStandingMode() { return; }
tryToBecomeBoostMode() { return; }
}
制作具有多个逻辑判断的物件:Hero
(Context 物件)
class Hero {
constructor() {
/** @type {State} */
this.state = new StandingState(this);
}
/** @param {State} state */
changeState(state) {
this.state = state;
}
getState() {
return this.state;
}
tryToShot() {
this.state.tryToShot();
}
shot() {
console.log("英雄射出一发子弹");
}
tryToKick() {
this.state.tryToKick();
}
kick() {
console.log("英雄向前方踢了一脚");
}
tryToJump() {
this.state.tryToJump();
}
jump() {
console.log("英雄往上跳");
}
tryToMovingToRight() {
this.state.tryToMovingToRight();
}
moveToRight() {
console.log("英雄往右走");
}
tryToMovingToLeft() {
this.state.tryToMovingToLeft();
}
moveToLeft() {
console.log("英雄往左走");
}
tryToDoSomething() {
this.state.tryToDoSomething();
}
doNothing() {
console.log("什麽事也没发生");
}
tryToBecomeSnipperMode() {
this.state.tryToBecomeSnipperMode();
}
tryToBecomeStandingMode() {
this.state.tryToBecomeStandingMode();
}
tryToBecomeBoostMode() {
this.state.tryToBecomeBoostMode();
}
}
制作独立物件的子代:StandingState
、SnipperState
、BoostState
(State 物件)
class StandingState extends State {
/** @param {Hero} hero */
constructor(hero) {
super(hero);
}
/** @override */
tryToShot() {
this.hero.shot();
}
/** @override */
tryToKick() {
this.hero.kick();
}
/** @override */
tryToJump() {
this.hero.jump();
}
/** @override */
tryToMovingToRight() {
this.hero.moveToRight();
}
/** @override */
tryToMovingToLeft() {
this.hero.moveToLeft();
}
/** @override */
tryToDoSomething() {
this.hero.doNothing();
}
/** @override */
tryToBecomeSnipperMode() {
this.hero.changeState(new SnipperState(this.hero));
}
/** @override */
tryToBecomeStandingMode() {
this.hero.changeState(new StandingState(this.hero));
}
/** @override */
tryToBecomeBoostMode() {
this.hero.changeState(new BoostState(this.hero));
}
}
class SnipperState extends State {
/** @param {Hero} hero */
constructor(hero) {
super(hero);
}
/** @override */
tryToShot() {
this.hero.shot();
}
/** @override */
tryToKick() {
this.hero.doNothing();
}
/** @override */
tryToJump() {
this.hero.doNothing();
}
/** @override */
tryToMovingToRight() {
this.hero.doNothing();
}
/** @override */
tryToMovingToLeft() {
this.hero.doNothing();
}
/** @override */
tryToDoSomething() {
this.hero.doNothing();
}
/** @override */
tryToBecomeSnipperMode() {
this.hero.changeState(new SnipperState(this.hero));
}
/** @override */
tryToBecomeStandingMode() {
this.hero.changeState(new StandingState(this.hero));
}
/** @override */
tryToBecomeBoostMode() {
this.hero.changeState(new BoostState(this.hero));
}
}
class BoostState extends State {
/** @param {Hero} hero */
constructor(hero) {
super(hero);
}
/** @override */
tryToShot() {
this.hero.doNothing();
}
/** @override */
tryToKick() {
this.hero.doNothing();
}
/** @override */
tryToJump() {
this.hero.jump();
}
/** @override */
tryToMovingToRight() {
this.hero.moveToRight();
}
/** @override */
tryToMovingToLeft() {
this.hero.moveToLeft();
}
/** @override */
tryToDoSomething() {
this.hero.doNothing();
}
/** @override */
tryToBecomeSnipperMode() {
this.hero.changeState(new BoostState(this.hero));
}
/** @override */
tryToBecomeStandingMode() {
this.hero.changeState(new StandingState(this.hero));
}
/** @override */
tryToBecomeBoostMode() {
this.hero.changeState(new BoostState(this.hero));
}
}
制作掌机的摇杆:VideoGameController
class VideoGameController {
/** @param {Hero} hero */
constructor(hero) {
this.hero = hero;
}
pushButtonY() {
this.hero.tryToShot();
}
pushButtonX() {
this.hero.tryToKick();
}
pushButtonB() {
this.hero.tryToJump();
}
pushButtonA() {
this.hero.tryToDoSomething();
}
pushButtonRightArrow() {
this.hero.tryToMovingToRight();
}
pushButtonLeftArrow() {
this.hero.tryToMovingToLeft();
}
holdButtonR() {
this.hero.tryToBecomeSnipperMode();
}
releaseButtonR() {
this.hero.tryToBecomeStandingMode();
}
holdButtonL() {
this.hero.tryToBecomeBoostMode();
}
releaseButtonL() {
this.hero.tryToBecomeStandingMode();
}
}
测试,模拟玩家不断在不同模式间尝试按钮:VideoGameStateSample
/** @param {VideoGameController} controller */
const standardOperation = (controller) => {
controller.pushButtonY();
controller.pushButtonX();
controller.pushButtonB();
controller.pushButtonA();
controller.pushButtonRightArrow();
controller.pushButtonLeftArrow();
};
const videoGameStateSample = () => {
const hero = new Hero();
const controller = new VideoGameController(hero);
console.log("---尝试基本按钮---");
standardOperation(controller);
console.log("\n---按住按钮 R---");
controller.holdButtonR();
standardOperation(controller);
console.log("\n---放开按钮 R---");
controller.releaseButtonR();
standardOperation(controller);
console.log("\n---按住按钮 L---");
controller.holdButtonL();
standardOperation(controller);
console.log("\n---放开按钮 L---");
controller.releaseButtonL();
standardOperation(controller);
};
videoGameStateSample();
State 模式有趣的地方,在於要改善方法内因为过多的 if - else if - else
或 switch case
导致程序码过多的情况,因为过多的程序码通常暗示着「难以改动」的可能,违反追求高弹性、易於修改的理念。
实作时观察到有三点要多加注意:
State
的虚拟层规范的方法足以用於所有的 State
?Context
在呼叫方法时,可以连带更改自己的状态?然而,就我自己的开发经验,使用的 if - else if - else
或 switch case
不一定是糟糕的做法,假如逻辑判断的数量只有几个的话,反而不用特地抽离、制作成 State
物件,避免会增加开发上的麻烦。
讲来讲去可以发现,什麽情况下需要转换,会是更重要的问题。
我想一个好的开发者如果能了解现在以及往後的可能需求,将更有把握判断要不要转换。
最後,尝试使用後明显感受到,那些依赖状态的变换而有不同程序码要执行的方法们,不用再写一大堆判断了,单单一、两行就做到相同的事情,这感觉真棒!
明天将介绍 Behavioural patterns 的第九个模式:Strategy 模式。
>>: Day26:今天来聊一下使用资料连接器将资料连接到Azure Sentinel
我们今天要把环境给设定好,并且尝试将laravel专案启动,而在我写这篇文章之前,我已经有利用Hom...
延续昨天要讲的 DS.php https://ithelp.ithome.com.tw/articl...
时间过好快,不知不觉的已经要迈入铁人赛的最後一天了 回顾开赛到现在,除了学习Shioaji API的...
这次要来实作指令decoder,负责pipeline中的decode stage。计组教科书上常见的...
前言 在更新Linkedkin 个人档案的时候 偶然发现他有技术检定测验 如果总成绩在前30%,会发...