啥是状态模式
状态模式是一种行为型设计模式,它允许一个对象在其内部状态发生改变时改变其行为。状态模式将对象的状态封装成不同的类,并使得对象在运行时可以动态地改变状态,从而改变对象的行为。状态模式的主要目的是促进代码的复用和灵活性。
状态模式中包含以下几个角色:
-
上下文(Context):上下文是包含了多个状态的对象,它提供了一个接口用于客户端访问状态。
-
抽象状态(State):抽象状态是所有具体状态的父类或接口,它定义了所有具体状态所需实现的方法。
-
具体状态(Concrete State):具体状态是继承自抽象状态的子类,它实现了抽象状态所定义的方法并且根据自身状态来处理上下文的请求。
当一个对象的内部状态发生变化时,该对象会将请求委托给与当前状态相应的具体状态进行处理。这样,对象的行为就会随着状态的改变而改变,从而避免了大量的if-else语句。状态模式还可以通过添加新的具体状态类来扩展一个系统,并且符合开闭原则。
常见的应用场景
状态模式的应用场景通常包括以下几个方面:
-
对象的行为随着内部状态的改变而改变。例如,在开发一个订单系统时,订单的状态可能包括已生成、已支付、已发货、已完成等不同的状态,针对不同的状态,订单对象会执行不同的操作。
-
代码中包含大量的条件语句。例如,当需要判断用户的登录状态(已登录或未登录)时,往往需要使用大量的if-else语句来处理,而使用状态模式可以将这些条件语句转化为不同的状态类,从而增强了代码的可维护性和可扩展性。
-
需要在运行时根据一个对象的状态来改变其行为。例如,在一款游戏中,角色的状态可能包括生命值、魔法值等属性,当某个属性值发生变化时,角色的行为也会相应地改变。
-
对象的状态转换次数比较频繁。例如,在一个电商平台上,商品的状态可能会经常发生变化,从"待审核"到"审核通过"再到"已上架"等多个状态之间进行切换,此时使用状态模式可以优雅地处理这种状态转换过程。
综上所述,状态模式是一种非常实用的设计模式,可以使得对象的行为更具灵活性和易维护性,适合处理复杂的对象状态转换问题。
例子1
假设有一个用户管理系统,需要判断当前用户是否已经登录。
使用状态模式
通过不同状态去执行不同逻辑。
首先定义一个状态接口LoginState,并在接口中定义了两个方法:
java
public interface LoginState {
void handle();
String getStateName();
}
其中handle()方法表示处理该状态的方法,getStateName()方法返回该状态的名称。然后定义两个具体状态类:LoggedInState和LoggedOutState。
java
public class LoggedInState implements LoginState {
@Override
public void handle() {
System.out.println("当前用户已登录");
}
@Override
public String getStateName() {
return "已登录";
}
}
public class LoggedOutState implements LoginState {
@Override
public void handle() {
System.out.println("当前用户未登录");
}
@Override
public String getStateName() {
return "未登录";
}
}
接下来定义一个用户类User,用于保存当前用户的状态,并提供一个方法getUserState()用于获取当前用户的状态。
java
public class User {
private LoginState state;
public User(LoginState state) {
this.state = state;
}
public void setState(LoginState state) {
this.state = state;
}
public LoginState getUserState() {
return state;
}
}
最后,在客户端代码中,可以根据当前用户的状态来执行不同的操作:
java
public static void main(String[] args) {
User user = new User(new LoggedOutState()); // 初始状态为未登录
// 根据当前用户状态执行不同的操作
user.getUserState().handle();
}
运行上述代码,输出结果如下:
当前用户未登录
如果将User对象的状态改为已登录,再次运行代码,输出结果如下:
当前用户已登录
通过使用状态模式,我们可以将原本大量的if-else语句转换成不同的状态类,从而使得代码更加清晰易懂、易维护。
不使用状态模式
如果不使用状态模式,我们可能会在代码中使用很多的if-else语句来判断当前用户的状态。例如,在用户管理系统中,可能会有很多个地方需要根据当前用户是否已登录来执行不同的逻辑,这时候就会写出很多类似下面这样的代码:
java
User user = getCurrentUser();
if (user != null && user.isLoggedIn()) {
// 执行已登录用户的操作
} else {
// 执行未登录用户的操作
}
这样的代码虽然简单,但是如果在多处都要进行状态判断,就会导致代码冗余度很高、可读性差、扩展性也不好。
而使用状态模式,我们可以将状态的处理逻辑封装到不同的状态类中,从而避免了大量的if-else语句。此外,状态模式还可以让我们添加新的状态变得更加容易,只需要添加一个新的具体状态类即可。因此,使用状态模式来处理对象的状态会使我们的代码更加清晰、灵活、易于维护和扩展。
例子2
需要在运行时根据一个对象的状态来改变其行为。例如,在一款游戏中,角色的状态可能包括生命值、魔法值等属性,当某个属性值发生变化时,角色的行为也会相应地改变。
假设有一个名为Character的类表示游戏中的角色。该类包含以下属性:
- healthPoints:角色的生命值
- magicPoints:角色的魔法值
- currentState:角色当前的状态(存活、昏迷、死亡等)
当角色的生命值或魔法值发生变化时,角色的状态也会随之发生改变,从而影响角色的行为。
首先,我们需要定义状态接口和具体状态类:
java
// 状态接口
public interface CharacterState {
void handle();
}
// 具体状态类 - 存活状态
public class AliveState implements CharacterState {
private Character character;
public AliveState(Character character) {
this.character = character;
}
@Override
public void handle() {
// 在存活状态下,角色可以执行各种行为
System.out.println("角色处于存活状态,可以执行各种行为");
}
}
// 具体状态类 - 昏迷状态
public class UnconsciousState implements CharacterState {
private Character character;
public UnconsciousState(Character character) {
this.character = character;
}
@Override
public void handle() {
// 在昏迷状态下,角色无法执行任何行为
System.out.println("角色处于昏迷状态,无法执行任何行为");
}
}
// 具体状态类 - 死亡状态
public class DeadState implements CharacterState {
private Character character;
public DeadState(Character character) {
this.character = character;
}
@Override
public void handle() {
// 在死亡状态下,角色无法执行任何行为
System.out.println("角色已死亡,无法执行任何行为");
// 当生命值超过0时,进入昏迷状态
if (character.getHealthPoints() > 0) {
character.changeState(new UnconsciousState(character));
}
}
}
然后,我们需要定义Character类,并在其中保存当前状态,并提供相应的操作方法来改变角色状态:
java
public class Character {
// 角色属性
private int healthPoints; // 生命值
private int magicPoints; // 魔法值
// 角色状态
private CharacterState state;
public Character(int healthPoints, int magicPoints) {
this.healthPoints = healthPoints;
this.magicPoints = magicPoints;
this.state = new AliveState(this);
}
// 改变角色状态
public void changeState(CharacterState newState) {
this.state = newState;
}
// 获取当前角色状态
public CharacterState getCurrentState() {
return this.state;
}
// 角色受到伤害
public void takeDamage(int damage) {
healthPoints -= damage;
if (healthPoints <= 0) {
// 如果生命值小于等于0,则进入死亡状态
changeState(new DeadState(this));
} else if (healthPoints < 10) {
// 如果生命值低于一定值,则进入昏迷状态
changeState(new UnconsciousState(this));
}
}
// 角色恢复生命值
public void restoreHealth(int amount) {
healthPoints += amount;
if (state instanceof UnconsciousState && healthPoints >= 10) {
// 如果之前处于昏迷状态,但现在生命值超过一定值,则恢复为存活状态
changeState(new AliveState(this));
}
}
// 角色消耗魔法值
public void consumeMagic(int amount) {
magicPoints -= amount;
if (magicPoints < 0) {
// 如果魔法值小于0,则进入昏迷状态
changeState(new UnconsciousState(this));
}
}
// 角色恢复魔法值
public void restoreMagic(int amount) {
magicPoints += amount;
if (state instanceof UnconsciousState && magicPoints >= 10) {
// 如果之前处于昏迷状态,但现在魔法值超过一定值,则恢复为存活状态
changeState(new AliveState(this));
}
}
}
最后,我们可以创建一个Character对象并测试其行为变化:
java
public class Main {
public static void main(String[] args) {
// 20点生命值,10点魔法值
Character character = new Character(20, 10);
character.getCurrentState().handle(); // 输出:角色处于存活状态,可以执行各种行为
character.takeDamage(25); // 角色受到25点伤害,进入死亡状态
character.getCurrentState().handle(); // 输出:角色已死亡,无法执行任何行为
character.restoreHealth(10); // 角色恢复10点生命值,但无法改变状态
character.getCurrentState().handle(); // 输出:角色已死亡,无法执行任何行为
character.restoreMagic(20); // 角色恢复20点魔法值,从死亡状态进入昏迷状态
character.getCurrentState().handle(); // 输出:角色处于昏迷状态,无法执行任何行为
character.consumeMagic(5); // 角色消耗5点魔法值,仍然处于昏迷状态
character.getCurrentState().handle(); // 输出:角色处于昏迷状态,无法执行任何行为
character.restoreHealth(15); // 角色恢复15点生命值,从昏迷状态进入存活状态
character.getCurrentState().handle(); // 输出:角色处于存活状态,可以执行各种行为
}
}