自动驾驶谈谈『状态模式』

目录:设计模式之小试牛刀
源码路径:Github-Design Pattern


定义(State Pattern):

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

类图:

状态模式通用类图

启示:

最近几年,自动驾驶被炒的是热火朝天,国外的知名google、uber、tesla,国内的百度、乐视也都投入到了自动驾驶的浪潮中去。像google和tesla的自动驾驶技术已经相对成熟,可以上路了。比较火的纯电动支持自动驾驶的特斯拉可谓是红极一时,官网号称所有车型均搭载全自动驾驶硬件。自动驾驶作为一种发展趋势,相信在不久的将来,我们每个人都能享受到这科技的红利。

说到科技,我们就来简单探讨下自动驾驶涉及到的技术。首先,这一切都需要必不可少的硬件支持,比如传感器、摄像头、雷达。有了硬件就可以进行数据采集,收集驾驶过程中的各种信息,通过算法进行数据分析,来控制当前的驾驶行为。
换句话说,自动驾驶是结合外部因素和内部因素通过算法分析来控制驱动的。外部因素无外乎道路状况、交通标志等。内部因素比如电池容量、车况等等。

那这跟我们这节讲的状态模式有什么区别呢?
在我理解,自动驾驶也是状态和行为相结合的。
比如摄像头检测到路边的限速标记,自动控制车速,比如摄像头检测到前方十字路口交通信号灯为红灯,则停车等待。
其中限速标记和交通灯都是一个状态的体现,控制车速和停车等待则是状态驱动的结果。但是呢,这些状态行为并不符合我们状态模式的定义(当一个对象内在状态改变时允许其改变行为)。很显然限速标记和交通灯是外部状态。

别失望,我们来找找特斯拉的内部状态。作为一辆车它的状态其实很简单,也就是运行、加速、减速、停车状态。在停车状态,我们可以控制它启动运行,切换至运行状态;在运行状态,自动驾驶根据行驶条件控制加速减速,切换至加速减速状态;到达目的地后,泊车切换到停车状态。

代码:

这个应用场景其实很简单,按照我们传统的写法,我们肯定几个if..else或用switch..case就搞定了。的确,但是呢,传统的写法就扩展性和代码结构上就十分差强人意。废话不多说,下面我们就以特斯拉自动驾驶为例,分析下状态模式的应用。

首先我们定义一个状态接口:

    /// <summary>
    /// 状态接口类
    /// </summary>
    public interface ICarState
    {
        /// <summary>
        /// 启动
        /// </summary>
        void Drive(Car car);

        /// <summary>
        /// 停车
        /// </summary>
        void Stop(Car car);

        /// <summary>
        /// 加速
        /// </summary>
        /// <param name="car"></param>
        void SpeedUp(Car car);

        /// <summary>
        /// 减速
        /// </summary>
        /// <param name="car"></param>
        void SpeedDown(Car car);
    }

然后我们依次实现这四种状态。
运行状态下可以切换到其他三种状态。

/// <summary>
/// 运行状态
/// </summary>
public class RuningState : ICarState
{
    public void Drive(Car car)
    {
        Console.WriteLine("车辆正在自动驾驶!");
    }

    public void Stop(Car car)
    {
        Console.WriteLine("车辆已停止!");
        car.CurrentCarState = Car.StopState;
    }

    public void SpeedUp(Car car)
    {
        Console.WriteLine("路况良好,开始加速行驶!");
        car.CurrentCarState = Car.SpeedUpState;
    }

    public void SpeedDown(Car car)
    {
        Console.WriteLine("路况一般,开始加速行驶!");
        car.CurrentCarState = Car.SpeedDownState;
    }
}

停车状态下,只能切换到启动状态,不可加速减速。

/// <summary>
/// 停车状态
/// </summary>
public class StopState : ICarState
{
    public void Drive(Car car)
    {
        Console.WriteLine($"{car.Name}已启动,开始自动驾驶!");
        car.CurrentCarState = Car.RunState;
    }

    public void Stop(Car car)
    {
        Console.WriteLine("车辆已停止!");
    }

    public void SpeedUp(Car car)
    {
        Console.WriteLine("车辆已停止!");
    }

    public void SpeedDown(Car car)
    {
        Console.WriteLine("车辆已停止!");
    }
}

加速状态也是运行状态,可减速或停车。

/// <summary>
/// 加速状态
/// </summary>
public class SpeedUpState : ICarState
{
    public void Drive(Car car)
    {
        Console.WriteLine("车辆正在自动驾驶!");
    }

    public void Stop(Car car)
    {
        Console.WriteLine("车辆已停止!");
        car.CurrentCarState = Car.StopState;
    }

    public void SpeedUp(Car car)
    {
        Console.WriteLine("车辆正在加速行驶!");
    }

    public void SpeedDown(Car car)
    {
        Console.WriteLine("路况一般,减速行驶!");
        car.CurrentCarState = Car.SpeedDownState;
    }
}

减速状态也是运行状态,可加速或停车。

/// <summary>
/// 减速状态
/// </summary>
public class SpeedDownState : ICarState
{
    public void Drive(Car car)
    {
        Console.WriteLine("车辆正在自动驾驶!");
    }

    public void Stop(Car car)
    {
        Console.WriteLine("车辆已停止!");
        car.CurrentCarState = Car.StopState;
    }

    public void SpeedUp(Car car)
    {
        Console.WriteLine("路况良好,加速行驶!");
        car.CurrentCarState = Car.SpeedUpState;
    }

    public void SpeedDown(Car car)
    {
        Console.WriteLine("车辆正在减速行驶!");
    }
}

定义完状态,下面我们就来看看实际的车类。

public class Car
{
    public string Name { get; set; }

    public Car()
    {
        this.CurrentCarState = StopState;//初始状态为停车状态
    }

    internal static ICarState StopState = new StopState();
    internal static ICarState RunState = new RuningState();
    internal static ICarState SpeedDownState = new SpeedDownState();
    internal static ICarState SpeedUpState = new SpeedUpState();

    public ICarState CurrentCarState { get; set; }

    public void Run()
    {
        this.CurrentCarState.Drive(this);
    }

    public void Stop()
    {
        this.CurrentCarState.Stop(this);
    }

    public void SpeedUp()
    {
        this.CurrentCarState.SpeedUp(this);
    }
    public void SpeedDown()
    {
        this.CurrentCarState.SpeedDown(this);
    }
}

Car类也比较简单,主要是预先申明并实例化了几种状态并暴露设置当前状态的属性,以及提供了状态对应的行为方法,并委托给具体的状态去执行相应的动作。

下面就是简单的场景类了。

static void Main(string[] args)
{
    Car tesla = new Car() {Name = "特斯拉 Model S"};
    tesla.Run();
    tesla.SpeedUp();
    tesla.SpeedDown();
    tesla.Stop();

    Console.WriteLine();
}

运行结果

总结:

状态模式隐藏了具体的状态变化,但行为还是由状态变化驱动的。
就状态模式而言,其实就仅仅三个角色:

  • State——抽象状态角色:接口或抽象类,负责定义对象的所有状态对应的行为由具体状态去实现。这里对应的是我们定义的ICarState
  • ConcreteState——具体状态角色:处理当前状态的行为,决定是否可以过渡到其他状态。这里对应的是我们定义的RunStateStopStateSepeedUpStateSepeedDownState
  • Context——环境角色:定义行为,状态转换。这里对应的就是我们的Car类。

优缺点:

优点:结构清晰;符合OCP和SRP;封装性好。
缺点 :在状态过多的情况下,会导致具体状态类的膨胀。

应用场景:

  1. 行为随状态改变而改变。
  2. 状态已确定,且状态不宜过多。
  3. 重构if..elseswitch..case的不二之选。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容