一起谈.NET技术,走向ASP.NET架构设计——第五章:业务层模式,原则,实践(后篇)

  设计模式

  本篇文章主要是接着讨论的在业务层可以采用的或者常用的一些设计模式:

  State模式

  状态模式允许一个对象在随着它的状态变化而改变它自身的一些行为。

  在项目开发的过程中,有一些类,例如一个业务类常常是有自己的一些状态的,而且还存在状态之间的一些转换,有些状态之间是可以进行转换的,有些状态之间是不能转换的。就拿一个汽车来举例子,汽车有很多的状态:静止,启动,前进,后退,停车。而且不能由”前进”状态转为“启动”状态。

  很多朋友知道state模式的用法和结构,朋友们应该也清楚在状态之间的转换用swtich.. case的一些弊端。在项目中,很多时候就没有”一定”,”非得”要用state模式来解决类似的问题,即使可以用state模式来解决。如果变化不大,switch.. case就够用了。

  下面还是来首先来看看使用state模式的一些例子。

  还是采用电子商务为背景来举例:每一个订单都是有状态的:New(新的),Shipped(已经发货),Canceled(已取消)。我们知道一个新的订单可以被变为”取消”的状态,也可以成为”已发货”状态。但是订单不能从”已发货”状态,变为”取消”的状态。

  下面就是例子中的类图:

一起谈.NET技术,走向ASP.NET架构设计——第五章:业务层模式,原则,实践(后篇)

  首先还是创建一个名为:ASPPatterns.Chap5.StatePattern的解决方案,添加一个名为:ASPPattern.Chap5.StatePattern.Model的类库:

一起谈.NET技术,走向ASP.NET架构设计——第五章:业务层模式,原则,实践(后篇)

  然后添加在Model的类库中添加一个表示状态接口的定义:IOrderState:


public interface IOrderState
{
bool CanShip(Order Order);
void Ship(Order Order);

bool CanCancel(Order Order);
void Cancel(Order order);

OrderStatus Status {
get; }
}

  下面来定义个表示订单状态的枚举:


public enum OrderStatus
{
New
= 0,
Shipped
= 1,
Canceled
= 2
}

  然后我们来看看,真正要进行状态转化的那个订单类:


public class Order
{
private IOrderState _orderState;

public Order(IOrderState baseState)
{
_orderState
= baseState;
}

public int Id { get; set; }

public string Customer { get; set; }

public DateTime OrderedDate { 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)
{
_orderState
= OrderState;
}
}

  其实状态模式一个最主要的思想就是:把状态之间的转换分离出来,把每一个状态分解为一个个的状态的类,然后这些状态来负责如何在不同的状态之间的转换。也就是说,就本例而言,以前我们会在Order类中写上如下的语句:


public void ChangeStatus()
{
switch (Status)
{
case OrderStatus.New:
//.... do some things
break;
case OrderStatus.Shipped:
//.... do some things
break;

case OrderStatus.Canceled:
//.... do some things
break;
}
}

  我们知道其实此时就是由Order类来控制了状态之间的转换。其实state模式的就是让那些“状态”变为一个“有思想”的状态类,这些类自己来负责如何以及何时转换到其他的状态,这样Order的“负担”就减轻了。还有一个就是大家常常会”批判”:如何要添加一个新的状态,那么ChangeStatus方法势必会变化,因为这个方法控制了状态之间的转换,如果把状态的转换逻辑分离出去,最好做到添加或者减少状态都不会影响现有的Order就更好了。

  下面的讲述有点直接,希望不熟悉state模式的朋友先“撑下去”J

  1、当创建一个订单的时。候,这个订单的状态肯定就是”New”(新的)。那么我们可能就传入一个表示“New”状态的类:OrderNewStatus:

  new Order(OrderNewState newStatus)

  OrderNewStatus把订单的状态标记为New

  我们知道:订单的状态为New的时候,状态可以向Canceled和Shipped转换,那么OrderNewStatus的定义可能如下:


public class OrderNewState : IOrderState
{
public bool CanShip(Order Order)
{
return true;
}

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

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

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

public void Cancel(Order order)
{
order.Change(
new OrderCanceledState());
}
public new Order(OrderNewState newStatus);
}

  2、当新创建的订单处理到一定的流程的时候,例如要发货了,此时要更新订单的状态,此时我们只要调用Order的ship方法就行了。其实此时我们就是在调用NewOrderStatus类的Ship方法,这个类的ship方法知道怎么做:先判断Order的是否可以向Shipped状态转换(调用CanShip方法),如果可以,那么就new一个OrderShippedStatus状态的类,然后用这个类去替换原来的NewOrderStatus(调用Order类的Change方法),这样,Order的状态就是Shipped了,但是Order完全不用管状态之间是如何变化的。


public class OrderShippedState : IOrderState
{
public bool CanShip(Order order)
{
return false;
}

public void Ship(Order order)
{
throw new NotImplementedException("You can't ship a shipped order!");
}

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

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

public void Cancel(Order order)
{
throw new NotImplementedException("You can't ship a shipped order!");
}

}

  3、Canceled状态也同理,我这里就不在赘述了。


public class OrderCanceledState : IOrderState
{
public bool CanShip(Order Order)
{
return false;
}

public void Ship(Order Order)
{
throw new NotImplementedException("You can't ship a cancelled order!");
}

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

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

public void Cancel(Order order)
{
throw new NotImplementedException("You can't ship a cancelled order!");
}
}

  我们用一个UML图来结束state模式的讲述:

一起谈.NET技术,走向ASP.NET架构设计——第五章:业务层模式,原则,实践(后篇)

  Strategy模式

  其实策略模式,我这里不打算作太多的讲述,其实这种模式大家到处可见,我们常常在ASP.NET中常常提起的Provider模式,其实就是策略模式的一种实现。

  大家看看结构图,基本上就明白了:

一起谈.NET技术,走向ASP.NET架构设计——第五章:业务层模式,原则,实践(后篇)

  在上述的图中Context依赖一个IStrategy接口,我们可以决定让Context使用IStrategy的任意的一个实现者:ConcreteStrategyA 或者ConcreteStrategyB。其实就是可以替换不同的实现者。可能大家在数据访问层那块有的体验更加的明显:定义一个IDataProvider,然后实现一个AdoDotNetProvider和一个LinqToSqlProvider。

  架构模式

  下面就来补充一些架构模式的知识,下文主要讲述:Layer Supertype模式(超类模式)

  超类模式就是定义一个基类,然后其他的所有的类都从这个类中继承。对于业务层而言,在超类中可能会定义一些通用的业务规则和验证方法,这样就这些代码被到处分散。也体现了继承的一个好处。

  下面我们就来看一个项目中的例子(电子商务为例),类结构如下:

一起谈.NET技术,走向ASP.NET架构设计——第五章:业务层模式,原则,实践(后篇)

  大家可以看到,所有的业务类都是从EntityBase继承的。


public abstract class EntityBase<T>
{
private T _Id;
private bool _idHasBeenSet = false;
private IList<string> _brokenRules = new List<string>();

public EntityBase()
{ }

public EntityBase(T Id)
{
this.Id = Id;
}

public T Id
{
get { return _Id; }
set
{
if (_idHasBeenSet)
ThrowExceptionIfOverwritingAnId();

_Id
= value;
_idHasBeenSet
= true;
}
}

private void ThrowExceptionIfOverwritingAnId()
{
throw new ApplicationException("You cannot change the id of an entity.");
}

public bool IsValid()
{
ClearCollectionOfBrokenRules();
CheckForBrokenRules();
return _brokenRules.Count() == 0;
}

protected abstract void CheckForBrokenRules();

private void ClearCollectionOfBrokenRules()
{
_brokenRules.Clear();
}

public IEnumerable<string> GetBrokenBusinessRules()
{
return _brokenRules;
}

protected void AddBrokenRule(string brokenRule)
{
_brokenRules.Add(brokenRule);
}
}

  在这个超类中,提供了保存每个业务类唯一标识的逻辑,并且确保这个标识一旦设定就不会被改变。而且这个超类还提供一些简单的验证逻辑。

  我们再来看看如何使用这个超类,下面定义了一个Customer的业务类:


public class Customer : EntityBase<long>
{
public Customer() { }

public Customer(long Id)
:
base(Id)
{ }

public string FirstName { get; set; }
public string LastName { get; set; }

protected override void CheckForBrokenRules()
{
if (String.IsNullOrEmpty(FirstName))
base.AddBrokenRule("You must supply a first name.");

if (String.IsNullOrEmpty(LastName))
base.AddBrokenRule("You must supply a last name.");
}
}

  在这个类中,我们定义了唯一标识的类型:long,而且还定义了一些业务规则:FirstName, LastName不为空。至于如何调用这些验证规则,在超类中已经实现了,此时业务类就“轻松”了很多—起码不用再次写那些相类似的代码了,实现了一定程度上的代码重用。

  今天就讲到这里了,不正确之处,还望朋友们指出,见谅!

  多谢支持!

   
上一篇:C#MD5算法


下一篇:用SHA1或MD5 算法加密数据(示例:对用户身份验证的简单实现)