设计模式-状态模式

241 阅读3分钟

状态模式:当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类

通俗的说,状态模式就是一个关于多态的设计模式。

如果一个对象有多种状态,并且每种状态下的行为不同,一般的做法就是在这个对象的各个行为中添加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状态判断,缺点是必然导致类增加,这也是多态不可避免的缺点。