【愚公系列】2021年12月 二十三种设计模式(二十)-状态模式(State Pattern)

简介: 【愚公系列】2021年12月 二十三种设计模式(二十)-状态模式(State Pattern)

文章目录

前言

一、状态模式(State Pattern)

二、使用步骤

角色

示例

总结

优点

缺点

使用场景

前言

设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现在中都有相应的原理来与之对应,每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是它能被广泛应用的原因。


提示:以下是本篇文章正文内容,下面案例可供参考


一、状态模式(State Pattern)

状态模式属于行为型模式,它允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。


状态模式主要解决的是当控制一个对象状态的条件表达式过于复杂时的情况。把状态的判断逻辑转移到表示不同状态的一系列类中,可以把复杂的判断逻辑简化。


二、使用步骤

角色

1、抽象状态(State)


状态模式的核心基类,它表示某种对象的不同状态;


2、具体状态(Concrete State)


实现抽象状态的具体状态类;


3、环境类(Context)


拥有状态的具体对象,该对象会根据内部不同的对象有不同的行为。


示例

image.png

命名空间StatePattern中包含抽象状态类State,代表水的3种状态,0度及以下时为SolidState固体状态,0到100度为LiquidState液体状态,100度及以上时为GasState气体状态,并且不同的状态可以改变水类Water中喝水Drink方法的行为。本案例尝试以水的3种不同状态来向大家阐述状态模式在实际开发中的应用。

public class Water {
    public State State { get; set; }
    public double Temperature { get; set; } = 0;
    public Water() {
        State = new SolidState();
        State.Water = this;
    }
    public Water Increase(int value) {
        State.Increase(value);
        return this;
    }
    public Water Reduce(int value) {
        State.Reduce(value);
        return this;
    }
    public Water Drink() {
        if (this.State is LiquidState) {
            Console.WriteLine("You can drink!");
        }
        else {
            Console.WriteLine("You can not drink!");
        }
        Console.WriteLine(Const.LINE_BREAK);
        return this;
    }
}

Water水类充当环境类,公开一个状态基类,并在内部维护一个温度。Increase调用State的升温,而Reduce调用State的降温,最后的Drink方法会因为水的状态的不同而拥有不同的行为。为了简化逻辑,在本例中,只有当水为液体时才能被饮用。

public abstract partial class State {
    public static Water Water { get; set; }
    protected static string StateName { private get; set; }
    public void Increase(int value) {
        if (value == 0) return;
        if (value < 0) throw new ArgumentException();
        OnStateChanging();
        Water._temperature += value;
        ChangeState();
    }
    public void Reduce(int value) {
        if (value == 0) return;
        if (value < 0) throw new ArgumentException();
        if (Water._temperature - value <= Const.ABSOLUTE_ZERO) {
            throw new UnReachableException();
        }
        OnStateChanging();
        Water._temperature -= value;
        ChangeState();
    }
}

抽象状态基类,首先公开一个水的引用,并在所有实现类中共享StateName状态名,Increase为水升高一个温度,而Reduce为水降温。

public abstract partial class State {
    private void ChangeState() {
        if (Water._temperature <= 0) {
            Water.State = new SolidState();
        }
        else if (Water._temperature > 0 && Water._temperature < 100) {
            Water.State = new LiquidState();
        }
        else {
            Water.State = new GasState();
        }
        OnStateChanged();
    }
    protected virtual void OnStateChanging() {
        Console.WriteLine(Const.ON_STATE_CHANGING);
        Console.WriteLine(
            string.Format(Const.TEMPERATURE_INFO,
                            Water._temperature, StateName));
    }
    protected virtual void OnStateChanged() {
        Console.WriteLine(Const.ON_STATE_CHANGED);
        Console.WriteLine(
            string.Format(Const.TEMPERATURE_INFO,
                            Water._temperature, StateName));
        Console.WriteLine(Const.LINE_BREAK);
    }
}

抽象状态基类的第2部分(partial ),定义ChangeState方法以在改变温度时更改状态,另外定义OnStateChanging和OnStateChanged这2个受保护的虚方法以便提供“子类可以决定是否重写相应的方法来影响父类”的这样一个功能(OOP特性)。

public class SolidState : State {
    public SolidState() {
        StateName = "Solid";
    }
}
public class LiquidState : State {
    public LiquidState() {
        StateName = "Liquid";
    }
}
public class GasState : State {
    public GasState() {
        StateName = "Gas";
    }
}

水的3种状态的具体实现类,SolidState固体状态、LiquidState液体状态和GasState气体状态,由于我们在状态基类中封装了较多的功能,所以此处的3个具体类都比较精简,只在构造函数中更改共享的StateName状态名称字段。在实际开发过程中,应当尽可能的将具体的功能封装在状态实现类中。

public class Const {
    public const double ABSOLUTE_ZERO = -273.15;
    public const string LINE_BREAK =
        "--------------------------------------------------";
    public const string ON_STATE_CHANGING = "OnStateChanging()";
    public const string ON_STATE_CHANGED = "OnStateChanged()";
    public const string TEMPERATURE_INFO = "The temperature is {0} °C" +
        " and state name is {1}!";
}

常量类,维护一些在本案例中经常使用到的字符串或数值。在实际开发过程中不应当有此类,应该将相应的常量放在具体要使用的类中。2017年,阿里发布《阿里巴巴Java开发手册》,其中有一节提到此准则,所有使用面向对象编程语言的开发人员都应当遵从。

public class UnReachableException : Exception {
    public UnReachableException()
        : base("Absolute zero cannot be reached!") {
    }
    public UnReachableException(string message, Exception innerException)
        : base(message, innerException) {
    }
}

绝对零度无法到达异常类UnReachableException,进行简单的异常处理。

public class Program {
    private static Water _water = new Water();
    public static void Main(string[] args) {
        try {
            _water.Increase(68)
                  .Drink()
                  .Increase(82)
                  .Drink()
                  .Reduce(90)
                  .Drink()
                  .Reduce(0)
                  .Reduce(80)
                  .Drink()
                  .Reduce(300)
                  .Drink();
        }
        catch (Exception ex) {
            Console.WriteLine(ex.Message);
            Console.WriteLine(Const.LINE_BREAK);
        }
        Console.ReadKey();
    }
}

以上是本案例的调用方代码,升温方法Increase、降温方法Reduce和喝水方法Drink经过特别的处理以支持方法链。以下是这个案例的输出结果:

OnStateChanging()
The temperature is 0 °C and state name is Solid!
OnStateChanged()
The temperature is 68 °C and state name is Liquid!
--------------------------------------------------
You can drink!
--------------------------------------------------
OnStateChanging()
The temperature is 68 °C and state name is Liquid!
OnStateChanged()
The temperature is 150 °C and state name is Gas!
--------------------------------------------------
You can not drink!
--------------------------------------------------
OnStateChanging()
The temperature is 150 °C and state name is Gas!
OnStateChanged()
The temperature is 60 °C and state name is Liquid!
--------------------------------------------------
You can drink!
--------------------------------------------------
OnStateChanging()
The temperature is 60 °C and state name is Liquid!
OnStateChanged()
The temperature is -20 °C and state name is Solid!
--------------------------------------------------
You can not drink!
--------------------------------------------------
Absolute zero cannot be reached!
--------------------------------------------------

总结

优点

1、封装了转换规则;

2、枚举可能的状态,在枚举状态之前需要确定状态种类;

3、将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为;

4、允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块;

5、可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。


缺点

1、状态模式的使用必然会增加系统类和对象的个数;

2、状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱;

3、状态模式对开闭原则的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态,而且修改某个状态类的行为也需修改对应类的源代码。


使用场景

1、行为随状态改变而改变的场景;

2、条件、分支语句的代替者。


相关文章
|
7月前
|
设计模式
设计模式之 State(状态模式)
设计模式之 State(状态模式)
43 0
|
3月前
|
设计模式
设计模式-工厂模式 Factory Pattern(简单工厂、工厂方法、抽象工厂)
这篇文章详细解释了工厂模式,包括简单工厂、工厂方法和抽象工厂三种类型。每种模式都通过代码示例展示了其应用场景和实现方法,并比较了它们之间的差异。简单工厂模式通过一个工厂类来创建各种产品;工厂方法模式通过定义一个创建对象的接口,由子类决定实例化哪个类;抽象工厂模式提供一个创建相关或依赖对象家族的接口,而不需要明确指定具体类。
设计模式-工厂模式 Factory Pattern(简单工厂、工厂方法、抽象工厂)
|
3月前
|
设计模式 Java
设计模式--适配器模式 Adapter Pattern
这篇文章介绍了适配器模式,包括其基本介绍、工作原理以及类适配器模式、对象适配器模式和接口适配器模式三种实现方式。
|
3月前
|
设计模式 Java 测试技术
Java设计模式-状态模式(18)
Java设计模式-状态模式(18)
|
4月前
|
设计模式 网络协议 Java
【十五】设计模式~~~行为型模式~~~状态模式(Java)
文章详细介绍了状态模式(State Pattern),这是一种对象行为型模式,用于处理对象在其内部状态改变时的行为变化。文中通过案例分析,如银行账户状态管理和屏幕放大镜工具,展示了状态模式的应用场景和设计方法。文章阐述了状态模式的动机、定义、结构、优点、缺点以及适用情况,并提供了Java代码实现和测试结果。状态模式通过将对象的状态和行为封装在独立的状态类中,提高了系统的可扩展性和可维护性。
【十五】设计模式~~~行为型模式~~~状态模式(Java)
|
5月前
|
设计模式 JavaScript Go
js设计模式【详解】—— 状态模式
js设计模式【详解】—— 状态模式
81 7
|
6月前
|
设计模式
设计模式-05建造者模式(Builder Pattern)
设计模式-05建造者模式(Builder Pattern)
|
6月前
|
设计模式
状态模式-大话设计模式
状态模式-大话设计模式
|
7月前
|
设计模式 安全 Java
【设计模式】JAVA Design Patterns——Curiously Recurring Template Pattern(奇异递归模板模式)
该文介绍了一种C++的编程技巧——奇异递归模板模式(CRTP),旨在让派生组件能继承基本组件的特定功能。通过示例展示了如何创建一个`Fighter`接口和`MmaFighter`类,其中`MmaFighter`及其子类如`MmaBantamweightFighter`和`MmaHeavyweightFighter`强制类型安全,确保相同重量级的拳手之间才能进行比赛。这种设计避免了不同重量级拳手间的错误匹配,编译时会报错。CRTP适用于处理类型冲突、参数化类方法和限制方法只对相同类型实例生效的情况。
【设计模式】JAVA Design Patterns——Curiously Recurring Template Pattern(奇异递归模板模式)
|
6月前
|
设计模式 存储
行为设计模式之状态模式
行为设计模式之状态模式