23种常用设计模式之状态模式

说明

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

应用场景

  • 行为随状态改变而改变的场景。
  • 条件、分支语句的代替者。

模式特征

角色 说明 举栗
上下文环境(Context) 它定义了客户程序需要的接口并维护一个具体状态角色的实例,将与状态相关的操作委托给当前的Concrete State对象来处理。 Context
抽象状态(State) 定义一个接口以封装使用上下文环境的的一个特定状态相关的行为。 Children
具体状态(Concrete State) 实现抽象状态定义的接口 Brother、YoungerBrother、Sister

代码实现

代码场景——吃梨:哥哥、弟弟、妹妹三个人在一起吃梨,一人吃一口,只能吃10口。

  • 上下文环境
public class Context {

    int time = 0;

    public void start() throws InterruptedException {
        new Brother().eatPear(this);
    }
}
  • 抽象状态
public interface Children {
    void eatPear(Context context) throws InterruptedException;
}
  • 具体状态一
public class Brother implements Children {

    @Override
    public void eatPear(Context context) throws InterruptedException {
        if (context.time <= 10){
            //模拟耗时
            Thread.sleep((long) (Math.random()*2000));
            System.out.println("哥哥吃了一口梨");
            context.time++;
            if (System.currentTimeMillis() % 2 == 0){
                new YoungerBrother().eatPear(context);
            } else {
                new Sister().eatPear(context);
            }
        } else {
            System.out.println("梨子吃完了");
        }
    }
}
  • 具体状态二
public class YoungerBrother implements Children {
    @Override
    public void eatPear(Context context) throws InterruptedException {
        if (context.time <= 10){
            //模拟耗时
            Thread.sleep((long) (Math.random()*2000));
            System.out.println("弟弟吃了一口梨");
            context.time++;
            if (System.currentTimeMillis() % 2 == 0){
                new Brother().eatPear(context);
            } else {
                new Sister().eatPear(context);
            }
        } else {
            System.out.println("梨子吃完了");
        }
    }
}
  • 具体状态三
public class Sister implements Children {
    @Override
    public void eatPear(Context context) throws InterruptedException {
        if (context.time <= 10){
            //模拟耗时
            Thread.sleep((long) (Math.random()*2000));
            System.out.println("妹妹吃了一口梨");
            context.time++;
            if (System.currentTimeMillis() % 2 == 0){
                new YoungerBrother().eatPear(context);
            } else {
                new Brother().eatPear(context);
            }
        } else {
            System.out.println("梨子吃完了");
        }
    }
}
  • 客户端测试
public class Client {
    public static void main(String[] args) throws InterruptedException {
        Context context = new Context();
        context.start();
    }
}
  • 结果
哥哥吃了一口梨
弟弟吃了一口梨
妹妹吃了一口梨
哥哥吃了一口梨
弟弟吃了一口梨
哥哥吃了一口梨
妹妹吃了一口梨
哥哥吃了一口梨
妹妹吃了一口梨
哥哥吃了一口梨
妹妹吃了一口梨
梨子吃完了

说明:代码中的Context就相当于梨子,梨子的状态从完整到果核,这就是一个状态的变更,10次被吃完,意味着就对应着11种状态,但这其中我们是用哥哥、弟弟、妹妹来处理不同的状态的,当达到果核态,则不再处理下去。在实际应用场景中,可能是一种状态就有一个对应的处理,或者一种处理应对多种状态(类似本例)

优缺点

优点
  • 封装了转换规则。
  • 枚举可能的状态,在枚举状态之前需要确定状态种类。
  • 将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。
  • 允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。
  • 可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。
缺点
  • 状态模式的使用必然会增加系统类和对象的个数。
  • 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。
  • 状态模式对"开闭原则"的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。
原创文章 67 获赞 31 访问量 5万+

猜你喜欢

转载自blog.csdn.net/u012534326/article/details/102944858