状态模式,就是用类表示状态,好处是能通过切换类来方便地改变对象的状态,如果需要新加状态时也不用修改之前的代码。
假设现在有一个金库,金库和警报中心相连,金库里有警铃和通话用的电话,还有个时钟监视着现在的时间,白天或晚上使用警铃或打电话的表现都不一样。
不使用State模式的伪代码如下:
警报系统的类{
    
    使用金库时被调用的方法(){
        if(白天){
            向警报中心报告使用记录
        }else if(晚上){
            向警报中心报告紧急事态
        }
    }
    警铃响起时被调用的方法(){
        向警报中心报告紧急事态
    }
    正常通话时被调用的方法(){
        if(白天){
            呼叫警报中心
        }else if(晚上){
            呼叫警报中心的留言电话
        }
    }
}
使用State模式的伪代码:
表示白天的状态的类{
    
    使用金库时被调用的方法(){
        
        向警报中心报告使用记录
    }
    警铃响起时被调用的方法(){
        向警报中心报告紧急事态
    }
    正常通话时被调用的方法(){
    
        呼叫警报中心		
    }
}
表示晚上的状态的类{
    
    使用金库时被调用的方法(){
    
        向警报中心报告紧急事态
        
    }
    警铃响起时被调用的方法(){
        向警报中心报告紧急事态
    }
    正常通话时被调用的方法(){
        
        呼叫警报中心的留言电话
    
    }
}
在使用了State模式后,我们用类表示白天还是晚上,在类的各个方法中就不需要if语句判断现在是白天还是晚上了。

public interface State {
    // 设置时间
    void doClock(Context context,int hour);
    // 使用金库
    void doUse(Context context);
    /**
     * 按下警铃
     * @param context
     */
    void doAlarm(Context context);
    /**
     * 正常通话
     * @param context
     */
    void doPhone(Context context);
}
表示白天的类
public class DayState implements State {
    private static DayState singleton = new DayState();
    private DayState(){
    }
    public static State getInstance(){
        return singleton;
    }
    @Override
    public void doClock(Context context, int hour) {
        if (hour < 9 || 17 <= hour){
            context.changeState(NightState.getInstance());
        }
    }
    @Override
    public void doUse(Context context) {
        context.recordLog("使用金库(白天)");
    }
    @Override
    public void doAlarm(Context context) {
        context.callSecurityCenter("按下警铃(白天)");
    }
    @Override
    public void doPhone(Context context) {
        context.callSecurityCenter("正常通话(白天)");
    }
    public String toString(){
        return "[白天]";
    }
}
表示晚上的类
public class NightState implements State {
    private static NightState singleton = new NightState();
    private NightState() {
    }
    public static NightState getInstance() {
        return singleton;
    }
    @Override
    public void doClock(Context context, int hour) {
        if (9 <= hour && hour < 17) {
            context.changeState(DayState.getInstance());
        }
    }
    @Override
    public void doUse(Context context) {
        context.callSecurityCenter("紧急:晚上使用金库!");
    }
    @Override
    public void doAlarm(Context context) {
        context.callSecurityCenter("按下警铃(晚上)");
    }
    @Override
    public void doPhone(Context context) {
        context.recordLog("晚上的通话录音");
    }
    public String toString() {
        return "[晚上]";
    }
}
上下文
public interface Context {
    /**
     * 设置时间
     * @param hour
     */
    void setClock(int hour);
    /**
     * 改变状态
     * @param state
     */
    void changeState(State state);
    /**
     * 联系警报中心
     * @param msg
     */
    void callSecurityCenter(String msg);
    /**
     * 在警报中心留下记录
     * @param msg
     */
    void recordLog(String msg);
}
SateFrame持有State
public class SateFrame extends Frame implements ActionListener, Context {
    /**
     * 显示当前时间
     */
    private TextField textClock = new TextField(60);
    /**
     * 显示警报中心的记录
     */
    private TextArea textScreen = new TextArea(10, 60);
    private Button buttonUse = new Button("使用金库");
    private Button buttonAlarm = new Button("按下警铃");
    private Button buttonPhone = new Button("正常通话");
    private Button buttonExit = new Button("结束");
    /**
     * 当前的状态
     */
    private State state = DayState.getInstance();
    public SateFrame(String title) {
        super(title);
        setBackground(Color.lightGray);
        setLayout(new BorderLayout());
        add(textClock, BorderLayout.NORTH);
        textClock.setEditable(false);
        add(textScreen, BorderLayout.CENTER);
        textScreen.setEditable(false);
        // 为界面添加按钮
        Panel panel = new Panel();
        panel.add(buttonUse);
        panel.add(buttonAlarm);
        panel.add(buttonPhone);
        panel.add(buttonExit);
        add(panel, BorderLayout.SOUTH);
        pack();
        show();
        buttonUse.addActionListener(this);
        buttonAlarm.addActionListener(this);
        buttonPhone.addActionListener(this);
        buttonExit.addActionListener(this);
    }
    @Override
    public void setClock(int hour) {
        String clockString = "现在时间是";
        if (hour < 10) {
            clockString += "0" + hour + ":00";
        } else {
            clockString += hour + ":00";
        }
        System.out.println(clockString);
        textClock.setText(clockString);
        state.doClock(this, hour);
    }
    @Override
    public void changeState(State state) {
        System.out.println("从" + this.state + "状态变为了" + state + "状态。");
        this.state = state;
    }
    @Override
    public void callSecurityCenter(String msg) {
        textScreen.append("call! " + msg + "\n");
    }
    @Override
    public void recordLog(String msg) {
        textScreen.append("record...." + msg + "\n");
    }
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println(e.toString());
        if (e.getSource() == buttonUse) {
            state.doUse(this);
        } else if (e.getSource() == buttonAlarm) {
            state.doAlarm(this);
        } else if (e.getSource() == buttonPhone) {
            state.doPhone(this);
        } else if (e.getSource() == buttonExit) {
            System.exit(0);
        } else {
            System.out.println("?");
        }
    }
}
测试类Main
public class Main {
    public static void main(String[] args) {
        SateFrame frame = new SateFrame("State Sample");
        while (true) {
            for (int hour = 0; hour < 24; hour++) {
                frame.setClock(hour);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
状态模式通常有如下几种角色:
State(状态)
Sate角色表示状态,定义了根据不同状态进行不同处理的API,对应示例中的State接口。
ConcreteState(具体状态)
表示各个具体状态,对应示例中的DayState和NightState
Context(状况、前后关系、上下文)
Context角色持有表示当前状态的ConcreteState角色。

再看看我在项目中是如何使用State模式的。
公司的数字化营销系统为了调动销售的积极性,面向过程而不是结果,设计了一套任务系统,当某些前置条件(如客户提交了售后)满足时系统会自动给销售发送任务。
由于任务的类型很多有十几种而且每种任务的结果表单都不同,当时一番讨论后采用了将任务结果表拆分成多个表的方式,比如A类型任务的任务结果表是t_a,B类型的
任务的任务结果表是t_b。。。。
随着任务种类越来越多,任务结果Service类的添加任务结果和查询任务结果方法中就充斥着大量的if/else逻辑判断后面难以维护,代码如下:
TaskResultProcessor相当于State接口
/**
 * Created by 2YSP on 2020/2/12.
 */
public interface TaskResultProcessor {
    /**
     * 插入任务结果
     *
     * @param taskResultDTO
     * @return
     */
    Integer doAddTaskResult(TaskResultDTO taskResultDTO);
    /**
     * 查询任务结果
     *
     * @param taskId
     * @param taskType
     * @return
     */
    TaskResultDTO doFindTaskResult(Integer taskId, TaskType taskType);
    /**
     * 更新业务员位置信息
     *
     * @param taskResultDTO
     * @param resultId
     */
    void updateSellerPosition(TaskResultDTO taskResultDTO, Integer resultId);
    /**
     * 支持的任务类型
     *
     * @return
     */
    List<TaskType> supportTaskType();
}

@Slf4j
@Service
@RequiredArgsConstructor
public class AdjustTaskResultProcessor implements TaskResultProcessor {
    private final AdjustTaskResultMapper adjustTaskResultMapper;
    @Override
    public Integer doAddTaskResult(TaskResultDTO taskResultDTO) {
       ...
    }
    
    @Override
    public TaskResultDTO doFindTaskResult(Integer taskId, TaskType taskType) {
      	...
    }
    @Override
    public void updateSellerPosition(TaskResultDTO taskResultDTO, Integer resultId) {
       ...
    }
    @Override
    public List<TaskType> supportTaskType() {
        return Lists.newArrayList(TaskType.ADJUST_PRICE);
    }
}
TaskResultProcessorHolder持有所有的任务结果处理器,利用构造方法注入在项目启动时将Bean注入到PROCESSOR_MAP中。
@Component
public class TaskResultProcessorHolder {
    private static final Map<TaskType, TaskResultProcessor> PROCESSOR_MAP = new ConcurrentHashMap<>();
    @Autowired
    public TaskResultProcessorHolder(List<TaskResultProcessor> taskResultProcessors) {
        taskResultProcessors.forEach(taskResultProcessor -> {
            List<TaskType> taskTypes = taskResultProcessor.supportTaskType();
            taskTypes.forEach(t -> PROCESSOR_MAP.put(t, taskResultProcessor));
        });
    }
    public Optional<TaskResultProcessor> getProcessor(TaskType taskType) {
        return Optional.ofNullable(PROCESSOR_MAP.get(taskType));
    }
}
SupplierTaskResultServiceImpl作为调用方,代码就简化了很多。
/**
 * @author Ship
 * @date 2020-01-08 17:10 任务结果service
 */
@Slf4j
@Service
public class SupplierTaskResultServiceImpl implements SupplierTaskResultService {
    @Autowired
    private SupplierTaskMapper supplierTaskMapper;
    @Autowired
    private MQSender mqSender;
    @Autowired
    private TaskResultProcessorHolder processorHolder;
    @Override
    public TaskResultDTO findTaskResult(Integer taskId) {
        SupplierTaskDTO supplierTaskDTO = supplierTaskMapper.queryById(taskId);
        Byte type;
        if (TaskSourceType.SYS_AUTO.getCode().equals(supplierTaskDTO.getSource())) {
            type = TaskType.EARLY_WARNING_PROCESSING.getValue();
        } else {
            type = supplierTaskDTO.getType();
        }
        // 直接调用任务结果处理器的方法即可
        TaskResultProcessor processor = getTaskResultProcessor(type);
        return processor.doFindTaskResult(taskId, TaskType.getTaskType(type));
    }
    @Override
    public int addTaskResult(TaskResultDTO taskResultDTO) {
        log.info("taskResultDTO--->{}", JSON.toJSON(taskResultDTO));
    	// 参数校验。。。
        taskResultDTO.setRecordTime(LocalDateTime.now());
        // 自动任务时,type不需要
        Byte type;
        if (TaskSourceType.SYS_AUTO.equals(taskResultDTO.getTaskSourceType())) {
            type = TaskType.EARLY_WARNING_PROCESSING.getValue();
        } else {
            type = taskResultDTO.getTaskType().getValue();
        }
        // 直接调用任务结果处理器的方法即可
        TaskResultProcessor processor = getTaskResultProcessor(type);
        Integer resultId = processor.doAddTaskResult(taskResultDTO);
        mqSender.updateSellerPosition(getSellerPositionDTO(taskResultDTO, resultId));
        return resultId;
    }
  
   
    @Override
    public void updateSellerPosition(SellerPositionDTO positionDTO) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start("异步更新任务结果中的业务员位置信息");
        TaskResultDTO taskResultDTO = new TaskResultDTO();
       	// 根据经纬度解析地址...
        Byte type;
        if (TaskSourceType.SYS_AUTO.getCode().equals(positionDTO.getTaskSource())) {
            type = TaskType.EARLY_WARNING_PROCESSING.getValue();
        } else {
            type = positionDTO.getTaskType();
        }
        TaskResultProcessor processor = getTaskResultProcessor(type);
        processor.updateSellerPosition(taskResultDTO, positionDTO.getResultId());
        stopWatch.stop();
        log.info(stopWatch.prettyPrint());
    }
    /**
     * 根据任务类型获取对应的结果处理器
     *
     * @param type
     * @return
     */
    private TaskResultProcessor getTaskResultProcessor(Byte type) {
        if (Objects.isNull(type)) {
            throw new BusinessException("任务类型不能为空");
        }
        TaskType taskType = TaskType.getTaskType(type);
        TaskResultProcessor processor = processorHolder.getProcessor(taskType)
                .orElseThrow(() -> new BusinessException("任务触发类型有误"));
        return processor;
    }
}
总之,状态模式的优缺点有这几个,要根据自己的实际场景决定是否使用。
优点:
缺点:
原文:https://www.cnblogs.com/2YSP/p/12772359.html