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();
}
}
}

State模式的经典应用场景:订单处理(c#实现)的更多相关文章

  1. ASP.NET运行时详解 集成模式和经典模式

    遗留问题 在<ASP.NET运行时详解 生命周期入口分析>中遗留两个问题,包括Application的InitInternal方法执行细节.IIS6和II7经典模式请求管道管理类Appli ...

  2. 敏捷软件开发(1)--- STATE 模式

    如果状态在运行过程中,不停的切换和改变,我们怎么办? 状态的迁移是我们生活和工程中非常普遍的一个概念.于是在数学上有一种理论来分析和解决这个问题. 有限状态机理论是一个非常成熟的理论,所有动作和流程的 ...

  3. state模式理解

    state模式应用场景 条件判断很多的情况 比如有很多if else语句:switch case语句等等. 如果以后业务越来越复杂,条件判断有100多个,每种条件的处理逻辑很复杂,不止一个业务逻辑会重 ...

  4. Java设计模式之《桥接模式》及应用场景

    摘要: 原创作品,可以转载,但是请标注出处地址http://www.cnblogs.com/V1haoge/p/6497919.html 这里摘抄一份他处的概念,你可以不必理会,先看下面得讲解与实例, ...

  5. Java设计模式(19)状态模式(State模式)

    State的定义:不同的状态,不同的行为:或者说,每个状态有着相应的行为. 何时使用状态模式 State模式在实际使用中比较多,适合"状态的切换".因为我们经常会使用If else ...

  6. 45个 GIT 经典操作场景,专治不会合代码

    大家好,我是小富~ 技术交流关注公众号:程序员内点事 传送门:原文地址 git对于大家应该都不太陌生,熟练使用git已经成为程序员的一项基本技能,尽管在工作中有诸如 Sourcetree这样牛X的客户 ...

  7. 无聊之作,RPGdemo制作(一)角色state模式

    今天周末,在家无事可做,不知道为什么,突发奇想,想写一个RPG游戏的demo玩玩.所以就记录一下. 第一步要做的是,为以后的角色类写一个state模式的类,考虑到可能以后会用到,所以用模版来实现, / ...

  8. State模式

    地铁十字转门 状态迁移表格. 起始状态 触发迁移的事件 终止状态  要执行的动作. Locked   Coin               UnLocked UnLock UnLocked Pass  ...

  9. Behavioral模式State模式

    1.意向 同意一个目标,然后改变其内部状态,改变它的行为. 对象似乎改变它的类别. 2.别名 状态对象(Objects for States) 3.动机 考虑一个表示网络连接的类TCPConnecti ...

随机推荐

  1. Beaglebone Black– 智能家居控制系统 LAS - 网页服务器 Node.js 、Web Service、页面 和 TCP 请求转 UDP 发送

    上一篇,纯粹玩 ESP8266,写入了 init.lua 能收发 UDP.这次拿 BBB 开刀,用 BBB host 一个 web server ,用于与用户交互,数据来自 ESP8266 的 UDP ...

  2. [SAP ABAP开发技术总结]消息处理Messages

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...

  3. Struts BaseAction工具类,封装Session,Request,Application,ModelDriven

    package com.ssh.shop.action; import java.io.InputStream; import java.lang.reflect.ParameterizedType; ...

  4. 学习Berkeley DB- 入门

    1 导言 首先,我们要了解Berkeley DB的一些基本特性,在IBM的开发网站上有篇文章对其有比较清晰的介绍: 这篇文章讲到了BDB的设计思想和核心数据结构.以及数据访问算法:并有常用函数使用范例 ...

  5. 没有Path的Binding

    当Binding源本身就是数据且不需要Path来指明时,可以设置Path的值为".",或直接省略Path.XAML中这个"."可以省略不写,但在C#代码中是不能 ...

  6. kakfa的常用命令总结

    Kafka的版本间差异较大,下面是0.8.2.1的操作方法 首先cd到kafaka的bin目录下;   #step1启动zookeeper服务 nohup bin/zookeeper-server-s ...

  7. LINQ之路 2:C# 3.0的语言功能(上)

    在上一篇的LINQ介绍中,我们已经看到了隐式类型变量var,扩展方法(extension method)和lambda表达式的身影.没错,他们正是LINQ技术的基石,是他们让LINQ的实现成为可能,并 ...

  8. Spring AOP执行方法

      execution(* springinaction.springidol.Instrument.play(..)) * 代表返回为任意类型 springinaction.springidol.I ...

  9. 2014 Multi-University Training Contest 5

    hdu4911 max(逆序数-k,0) #include <iostream> #include<stdio.h> #include<vector> #inclu ...

  10. OpenGL的glScalef缩放变换函数详解

    glScalef是openGL中的模型缩放函数,就是把当前矩阵与一个表示延各个轴对物体进行拉伸.压缩.反射的矩阵相乘.这个物体的每个点的x,y,z坐标与对应的xyz参数相乘. 先看函数定义void g ...