State模式的经典应用场景:订单处理(c#实现)场景描述遇到问题解决问题走起

State模式在对象内部状态发生变化的时候,改变自身的行为,这通常是通过切换内部状态对象实现的,对象将自身在各个状态的行为推给了状态对象,从而解开了行为与对象的依赖。

场景描述

在经典的订单处理场景中,订单其不同状态的时候变现了不同的行为,具体内容如下:

  1. 假如订单是一个新创建的订单,那么它可以被寄送,也可以被取消;
  2. 假如订单已经被寄送,那么它不可以被再次寄送,也不可以被取消;
  3. 假如订单已经被取消,那么它不可以被寄送,也不可以被取消。

上述内容中详细解释了订单状态和对应行为的关系。

遇到问题

对逻辑的第一映像,通常是通过if-else或者switch子句,通过订单内部的一个表示状态的属性,判断出当前订单是否可以寄送和取消。

可是这样严重影响了程序代码的课扩展性,想想一下,如果我需要添加一种叫做“已出库”的状态,此时订单表现为可以被取消但是不可以再次申请寄送,那我们就需要在if-else子句中添加新的逻辑;又或者我们需要改变业务规则寄送的订单可以在没有完成前取消,那么我们又需要对订单的实现代码做逻辑更改,很明显,这样对扩展性来说是一个大问题。

所以,我们的解决方案是将订单的行为推送到订单状态自身,这样即使扩展再多的订单状态或者对状态行为进行更改,也可以轻松应对,只对很少的类进行更改,并且不会牵涉到太多代码逻辑。

解决问题走起

首先创建一个订表示订单状态的枚举OrderStatus

namespace Pattern.State
{
    public enum OrderStatus
    {
        New=0,
        Shipped=1,
        Canceled=2
    }
}

然后创建一个借口IOrderState,定义订单的行为和保存订单的状态枚举值

namespace Pattern.State
{
    public interface IOrderState
    {
        bool CanShip(Order order);
        void Ship(Order order);
        bool CanCancel(Order order);
        void Cancel(Order order);
        OrderStatus Status { get; }
    }
}

接下来就是最重要的Order类

namespace Pattern.State
{
    public class Order
    {
        private IOrderState orderState;


        public Order(IOrderState orderState)
        {
            this.orderState = orderState;
        }


        public int Id { get; set; }
        public string CustomerName { get; set; }
        public string Address { get; set; }

        public OrderStatus Status()
        {
            return orderState.Status;
        }

        public bool CanCancel()
        {
            return orderState.CanCancel(this);
        }

        public void Cancel()
        {
            if (CanCancel())
            {
                orderState.Cancel(this);
            }
        }

        public bool CanShip()
        {
            return orderState.CanShip(this);
        }

        public void Ship()
        {
            if (CanShip())
            {
                orderState.Ship(this);
            }
        }

        internal void Change(IOrderState orderState)
        {
            this.orderState = orderState;
        }
    }
}

你可以看到,本来想象中的复杂了代码逻辑没有了,代码变得更易懂易扩展,因为我们将这些行为转到了IOrderState的子类中,单个子类只维护当前状态下订单的行为:

1.NewState

namespace Pattern.State
{
    public class NewState:IOrderState
    {
        public bool CanShip(Order order)
        {
            //some logic here
            return true;
        }

        public void Ship(Order order)
        {
            order.Change(new ShippedState());
        }

        public bool CanCancel(Order order)
        {
            return true;
        }

        public void Cancel(Order order)
        {
            order.Change(new CanceledState());
        }

        public OrderStatus Status
        {
            get { return OrderStatus.New; }
        }
    }
}

2.ShippedState

namespace Pattern.State
{
    public class ShippedState:IOrderState
    {
        public bool CanShip(Order order)
        {
            return false;
        }

        public void Ship(Order order)
        {
            throw new InvalidOperationException();
        }

        public bool CanCancel(Order order)
        {
            return false;
        }

        public void Cancel(Order order)
        {
            throw new InvalidOperationException();
        }

        public OrderStatus Status
        {
            get { return OrderStatus.Shipped }
        }
    }
}

3.CanceledState

namespace Pattern.State
{
    public class CanceledState:IOrderState
    {
        public bool CanShip(Order order)
        {
            return false;
        }

        public void Ship(Order order)
        {
            throw new InvalidOperationException();
        }

        public bool CanCancel(Order order)
        {
            return false;
        }

        public void Cancel(Order order)
        {
            throw new InvalidOperationException();
        }

        public OrderStatus Status
        {
            get { return OrderStatus.Canceled; }
        }
    }
}

最后我们创建一个OrderFactory

namespace Pattern.State
{
    public static class OrderFactory
    {
        public static Order CreateOrder(string customerName, string address)
        {
            IOrderState orderState = new NewState();
            Order order = new Order(orderState);
            return order;
        }
    }
}

最后,通过一个控制台应用程序来测试一下:

namespace Pattern.Console
{
    class Program
    {
        static void Main(string[] args)
        {
            Order order = OrderFactory.CreateOrder("小白哥哥", "天津市和平区");
            if (order.CanShip())
            {
                System.Console.WriteLine("订单当前可以寄送");
            }
            order.Ship();
            if (!order.CanShip())
            {
                System.Console.WriteLine("订单当前不可以寄送");
            }
            System.Console.ReadKey();
        }
    }
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏walterlv - 吕毅的博客

.NET 命令行参数包含应用程序路径吗?

发布于 2018-09-11 13:28 更新于 2018-09...

873
来自专栏菩提树下的杨过

ExtJs学习笔记(23)-ScriptTagProxy+XTemplate+WCF跨域取数据

ajax应用中跨域一直是一个非常麻烦的问题,目前也有一些解决办法,但要么比较麻烦,要么就不具备通用性,幸好ExtJs里的ScriptTagProxy提供了跨域读...

2338
来自专栏LinkedBear的个人空间

HttpClient入门及其应用 顶

这些需求可能或多或少的会发生在平时的开发中,针对每种情况,可能解决方案不止一种。本文将会使用HttpClient这种工具来讲解HttpClient的相关知识,以...

1001
来自专栏逸鹏说道

C#注册表情缘

记得当时刚接触C#的时候,喜欢编写各种小软件,而注册表系列和网络系列被当时的我认为大牛的必备技能。直到我研究注册表前一天我都感觉他是那么的高深。 今天正好有空,...

3398
来自专栏恰同学骚年

自己动手写一个简单的MVC框架(第一版)

  路由(Route)、控制器(Controller)、行为(Action)、模型(Model)、视图(View)

1322
来自专栏dotnet core相关

Bootstrap-3-Typeahead

是Bootstrap-3-Typeahead,不是Twitter open source的typeahead,两者用法有差异。外加如果配合原生的Bootstra...

1476
来自专栏linjinhe的专栏

LevelDB:使用介绍

Get 接口和 Put 接口比较像,除了 leveldb::ReadOptions 参数是用来控制读操作的,具体见链接指向的代码。

5625
来自专栏AhDung

C#程序防多开又一法

在Main()方法开始时遍历所有进程,获取每个进程的程序集GUID和PID,若发现有跟自己GUID相同且PID不同的进程,就勒令自身退出。

1713
来自专栏大内老A

事件(Event),绝大多数内存泄漏(Memory Leak)的元凶[上篇]

最近这两天一直在忙着为一个项目检查内存泄漏(Memory Leak)的问题,对相关的知识进行了一下简单的学习和探索,其间也有了一些粗浅的经验积累,今天特意写一篇...

2366
来自专栏张高兴的博客

张高兴的 Windows 10 IoT 开发笔记:无线收发芯片 nRF24L01

1072

扫码关注云+社区