趣谈状态模式

Posted nedulee

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了趣谈状态模式相关的知识,希望对你有一定的参考价值。

全文一共1543字,预计阅读时间10分钟

定义:

  状态模式(State),当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。
  只看这个定义的话,想必会一头雾水,其实状态模式解决的问题是:
  当控制了一个对象状态转换的表达式过于复杂时,我们可以把状态的判断逻辑转移到表示不同状态的一系列的类中。这样做可以使复杂的判断逻辑简化,同时使类的职责更加单一。

实例:

  假设每一个程序员会对应一个经验值(empiricalValue),我们会根据这个程序员的经验值,来评定这个程序员的职称,如MT,开发助理,初级程序员,中级程序员,高级程序员,专家。那么让你来完成这个程序,你会如何设计你的代码呢?
  相信有一部分人会写出和我一样的代码:
**
 * 程序员类.
 *
 * @author jialin.li
 * @date 2019-12-30 17:38
 */
public class Programmer {
    private int empiricalValue;

    public void setEmpiricalValue(int empiricalValue) {
        this.empiricalValue = empiricalValue;
    }

    public void evaluate() {
        if(empiricalValue < 100){
            System.out.println("MT");
        }else if(empiricalValue < 200){
            System.out.println("开发助理");
        }else if(empiricalValue < 300){
            System.out.println("初级程序员");
        }else if(empiricalValue < 400){
            System.out.println("中级程序员");
        }else if(empiricalValue < 500){
            System.out.println("高级程序员");
        }else if(empiricalValue < 600){
            System.out.println("技术专家");
        }
    }
}
/**
 * 客户端.
 *
 * @author jialin.li
 * @date 2019-12-30 18:28
 */
public class Main {
    public static void main(String[] args) {
        Programmer programmer = new Programmer();
        programmer.setEmpiricalValue(50);
        programmer.evaluate();
        programmer.setEmpiricalValue(150);
        programmer.evaluate();
        programmer.setEmpiricalValue(250);
        programmer.evaluate();
        programmer.setEmpiricalValue(350);
        programmer.evaluate();
        programmer.setEmpiricalValue(450);
        programmer.evaluate();
        programmer.setEmpiricalValue(550);
        programmer.evaluate();
    }
}

结果:

MT
开发助理
初级程序员
中级程序员
高级程序员
技术专家

这样的代码有什么问题?

  首先,evaluate方法充斥着大量的if/else,这个时候就要警惕,因为大量的if/else往往代表了该代码不符合开闭原则,每次修改或者新增条件,都会对原来的代码产生影响。
  其次,evaluate方法比较长,在面向对象编程中,方法的设计应该是短小且功能单一的,较长的方法往往意味着该方法不符合单一职责原则,或者是需要进行抽象。
  实际上,这段代码是用面向对象语言写出的面向过程的代码。这也是软件开发中常见的误区之一,并不是使用面向对象语言,写出的代码就是面向对象代码。
这个时候应该如何对上述代码进行优化?这就引出了我们今天要介绍的设计模式:状态模式,状态模式的结构相对简单,但是用法却十分巧妙:
技术图片

 

 
  这里将状态模式对应到我们的实例中,对经验值的判定和处理,可以被封装成一个个ConcreteState,将他们的处理方法抽象出来,就是State,而Context则起到一个维护当前状态的作用,接下来是我们修改后的代码:

代码:

/**
 * 状态处理接口.
 *
 * @author jialin.li
 * @date 2019-12-30 19:23
 */
public interface State {
    void handle(Programmer programmer);
}
/**
 * 程序员类.
 *
 * @author jialin.li
 * @date 2019-12-30 17:38
 */
public class Programmer {
    /**
     * 经验值
     */
    private int empiricalValue;
    /**
     * 当前状态
     */
    private State state = new MTState();


    public void setEmpiricalValue(int empiricalValue) {
        this.empiricalValue = empiricalValue;
    }

    public int getEmpiricalValue() {
        return empiricalValue;
    }

    public void setState(State state) {
        this.state = state;
    }

    public void handle() {
        state.handle(this);
    }
}
/**
 * MT.
 *
 * @author jialin.li
 * @date 2019-12-30 19:30
 */
public class MTState implements State {
    @Override
    public void handle(Programmer programmer) {
        int empiricalValue = programmer.getEmpiricalValue();
        if (empiricalValue < 100) {
            System.out.println("MT");
        } else {
            State juniorProgrammer = new JuniorProgrammer();
            programmer.setState(juniorProgrammer);
            juniorProgrammer.handle(programmer);
        }
    }
}
/**
 * 助理程序员.
 *
 * @author jialin.li
 * @date 2019-12-30 19:33
 */
public class ProgrammerAssistant implements State{
    @Override
    public void handle(Programmer programmer) {
        int empiricalValue = programmer.getEmpiricalValue();
        if(empiricalValue < 200){
            System.out.println("开发助理");
        }else{
            JuniorProgrammer juniorProgrammer = new JuniorProgrammer();
            programmer.setState(juniorProgrammer);
            juniorProgrammer.handle(programmer);
        }
    }
}
/**
 * 初级程序员.
 *
 * @author jialin.li
 * @date 2019-12-30 19:31
 */
public class JuniorProgrammer implements State {
    @Override
    public void handle(Programmer programmer) {
        int empiricalValue = programmer.getEmpiricalValue();
        if (empiricalValue < 300) {
            System.out.println("初级程序员");
        } else {
            State middleProgrammer = new MiddleProgrammer();
            programmer.setState(middleProgrammer);
            middleProgrammer.handle(programmer);
        }
    }
}
/**
 * 中级程序员.
 *
 * @author jialin.li
 * @date 2019-12-30 19:32
 */
public class MiddleProgrammer implements State {
    @Override
    public void handle(Programmer programmer) {
        int empiricalValue = programmer.getEmpiricalValue();
        if (empiricalValue < 400) {
            System.out.println("中级程序员");
        } else {
            SeniorProgrammer seniorProgrammer = new SeniorProgrammer();
            programmer.setState(seniorProgrammer);
            seniorProgrammer.handle(programmer);
        }
    }
}
/**
 * 高级程序员.
 *
 * @author jialin.li
 * @date 2019-12-30 19:34
 */
public class SeniorProgrammer implements State {
    @Override
    public void handle(Programmer programmer) {
        int empiricalValue = programmer.getEmpiricalValue();
        if (empiricalValue < 500) {
            System.out.println("高级程序员");
        } else {
            Professor professor = new Professor();
            programmer.setState(professor);
            professor.handle(programmer);
        }
    }
}
/**
 * @author jialin.li
 * @date 2019-12-30 19:35
 */
public class Professor implements State {
    @Override
    public void handle(Programmer programmer) {
        System.out.println("技术专家");
    }
}
/**
 * @author jialin.li
 * @date 2019-12-30 19:35
 */
public class Professor implements State {
    @Override
    public void handle(Programmer programmer) {
        System.out.println("技术专家");
    }
}
/**
 * 客户端.
 *
 * @author jialin.li
 * @date 2019-12-30 19:36
 */
public class Main {
    public static void main(String[] args) {
        Programmer programmer = new Programmer();
        programmer.setEmpiricalValue(50);
        programmer.handle();
        programmer.setEmpiricalValue(150);
        programmer.handle();
        programmer.setEmpiricalValue(250);
        programmer.handle();
        programmer.setEmpiricalValue(350);
        programmer.handle();
        programmer.setEmpiricalValue(450);
        programmer.handle();
        programmer.setEmpiricalValue(550);
        programmer.handle();
    }
}

结果:

MT
初级程序员
初级程序员
中级程序员
高级程序员
技术专家
 
  可以看出客户端代码基本没有变化,服务端代码变得更加灵活了。
  在Tomcat中,有一个LifecycleState枚举类,用于描述组件的生命周期状态,状态变更的时候,会进行上一个状态的判断,从而确定本次状态变更是否合法,这种设计也可以用在我们的状态模式中。
 
@Override
public final synchronized void init() throws LifecycleException {
    if (!state.equals(LifecycleState.NEW)) {
        invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
    }

    try {
        setStateInternal(LifecycleState.INITIALIZING, null, false);
        initInternal();
        setStateInternal(LifecycleState.INITIALIZED, null, false);
    } catch (Throwable t) {
        handleSubClassException(t, "lifecycleBase.initFail", toString());
    }
}
private void invalidTransition(String type) throws LifecycleException {
    String msg = sm.getString("lifecycleBase.invalidTransition", type, toString(), state);
    throw new LifecycleException(msg);
}
  其实万变不离其宗,在设计模式中,这个宗指的就是各种设计原则。学习设计模式的时候,要时刻联系设计原则,有一种说法是,真正精通设计模式的时候,是忘记所有的设计模式(有一种张无忌学太极剑的感觉)。

  期待您的关注、推荐、收藏,同时也期待您的纠错和批评。

以上是关于趣谈状态模式的主要内容,如果未能解决你的问题,请参考以下文章

尝试使用片段保存夜间模式状态

趣谈 32 种设计模式

网络协议趣谈TCP协议连接和状态

网络协议趣谈TCP协议连接和状态

网络协议趣谈HTTPS协议加密证书和工作模式

网络协议趣谈HTTPS协议加密证书和工作模式