状态模式:当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类
通俗的说,状态模式就是一个关于多态的设计模式。
如果一个对象有多种状态,并且每种状态下的行为不同,一般的做法就是在这个对象的各个行为中添加if-else或者switch-case语句,但更好的做法是为每种状态创建一个状态对象,使用状态对象替换掉这些条件判断语句,使得状态控制更加灵活,扩展性也更好。
举个例子,某个平台的用户有两种状态,一种是普通用户,一种是VIP用户,VIP会员享受很多权益,比如购物享受折扣等
-当普通用户点击享受折扣时,提示用户,折扣的权益只有VIP会员专享功能
-当VIP会员点击享受折扣时,下单立减
首先定义一个用户状态枚举类:
public enum State {
NORMAL,
VIP
}
NORMAL代表普通用户,VIP代表VIP用户
用户的功能接口:
public interface IUser {
void shopping();
}
用户状态切换接口:
public interface ISwitchState {
void purchaseVip();
void expire();
}
接口中定义了两个方法,purchaseVip()是购买VIP,用户状态改为VIP状态,expire是会员过期,用户状态改为普通用户状态
public class User implements IUser, ISwitchState {
private State state = State.NORMAL;
@Override
public void shopping() {
if (state == State.VIP) {
System.out.println("开始享受折扣");
} else {
System.out.println("享受折扣是VIP会员专属");
}
}
@Override
public void purchaseVip() {
state = State.PLUS;
}
@Override
public void expire() {
state = State.NORMAL;
}
}
用户类实现了IUser接口,IUser接口中的每个功能都需要判断用户是否为VIP会员,也就是说每个方法都有if (state == State.VIP) {} else {}语句,如果状态不止两种,还需要用上switch-case语句来判断状态,这就是不使用状态模式的弊端:
-判断用户状态会发生大量的分支判断语句,导致代码冗长
-当状态增加或者减少时,需要改动多个地方,违反开闭原则
接下来我们就用多态特性重构代码:
为每个状态新建一个状态类,普通用户:
class Normal implements IUser {
@Override
public void shopping() {
System.out.println("享受折扣是VIP用户专享");
}
}
VIP会员:
class Plus implements IUser {
@Override
public void shopping() {
System.out.println("享受折扣");
}
}
每个状态类都实现了IUser接口,在接口方法中实现自己特定的行为。
用户类:
class User implements IUser, ISwitchState {
IUser state = new Normal();
@Override
public void shopping() {
state.shopping();
}
@Override
public void purchaseVIP() {
state = new Plus();
}
@Override
public void expire() {
state = new Normal();
}
}
可以看到,丑陋的状态判断语句消失了,无论IUser接口中有多少个方法,User类都只需要调用状态类的对应方法即可。
客户端测试:
public class Client {
@Test
public void test() {
// 用户初始状态为普通用户
User user = new User();
// 输出:享受折扣是VIP专享功能
user.shopping();
// 用户购买 VIP 会员,状态改变
user.purchaseVIP();
// 输出:享受折扣
user.shopping();
// VIP 会员过期,变成普通用户,状态改变
user.expire();
// 输出:享受折扣是VIP专享功能
user.shopping();
}
}
可以看到,用户状态改变后,行为也随着改变了,这就是状态模式定义的由来,它的优点是:将与特定状态相关的行为封装到一个状态对象中,使用多态代替if-else或者switch-case状态判断,缺点是必然导致类增加,这也是多态不可避免的缺点。