走向.NET架构设计—第五章—业务层模式,原则,实践(前篇)

走向.NET架构设计第五章业务层模式,原则,实践(前篇)

  前言:不管是GOF23种设计模式,还是Flower的企业架构模式,相信很多的朋友知道或者听说过。在那些很经典的书中,对模式都做了很精辟的解释,本篇的目的在于看看这些模式如何应用在项目中的,并且给出一些代码的例子,小洋也希望大家能够真正的理解这些模式的思想,而不仅仅停留在代码结构和表面上。

  

本篇的议题如下:

架构模式

设计模式

设计原则

 

  

  在上一章中,我们讲述了有关业务层分层的一些知识,下面我们就来看看,在具体的业务层的设计中,我们可以采用哪些模式可以将业务层设计的更加的灵活!

 

  架构模式

  首先我们就来看看,如何更加有效的组织业务规则。

 

  Specification Pattern(需求规格模式)

 

  这个模式的使用方法就是:把业务规则放在业务类的外面,并且封装成为一个个返回boolean值的算法。这些一个个的业务规则的算法不仅仅便于管理和维护,并且还可以被重用,而且很方便的组织成为复杂的业务逻辑。

 

  下面我们就来看一个以在线租DVD的公司的例子。例子很简单,场景也很简单:判断一个用户是否可以租更多的DVD。下面就是我们设计的一个基本的类图。(大家肯定觉得一上来就看类图有点突兀,没有一步步的分析,其实我是想让大家知道,所讲的是个什么东西样子,之后大家再慢慢的理解)

 

  下面我们就开始做这个事情:
  public  interface ISpecification<T>
    {
         bool IsSatisfiedBy(T entity); 
    }

     public  class HasReachedMaxSpecification : ISpecification<Customer>
{
         public  bool IsSatisfiedBy(Customer entity)
        {
             return entity.TotalRentNumber >  5;
        }
}
 

   上面的代码,其实就是把一个个的业务规则抽象出来了。我们知道,在系统中,不管业务规则多么复杂,最后在进行业务逻辑判定的时候,最后的结果还是“是否通过”。所以在这里就进行了抽象。

 

  因为我们的例子是以一个在线租赁DVD为例子,用户可以来租赁DVD,其中也是有一定的规则的,例如,如果用户已经租了3DVD,那么我们就会考虑,这个用户时候还可以继续租DVD。至于根据什么判断:可能DVD公司规定一个人最多不能超过5盘,或者DVD公司认为某个用户的信誉不好等等。 

  下面我们就来定义个具体的业务规则:HasReachedRentalThresholdSpecification

  根据这个规则就决定一个用户是否可以租DVD   

 

ExpandedBlockStart.gif 代码
  public   class  HasReachedRentalThresholdSpecification : ISpecification < CustomerAccount >  
    {
        
public   override   bool  IsSatisfiedBy(CustomerAccount candidate)
        {       
            
return  candidate.NumberOfRentalsThisMonth  >=   5 ;        
        }
    }

  

  这个规则定义出来后,我们就在业务类中使用这个规则:    

 

ExpandedBlockStart.gif 代码

public class Customer

{

        private ISpecification<Customer> specification = null;

        public Customer()

      {

            specification = new HasReachedMaxSpecification();

        }

 

        public int TotalRentNumber { get; set; }

 

        public bool CanRent()

        {

            return !specification.IsSatisfiedBy(this);

        }

}

  

  当然,我们可以把更多的业务规则组合进来。

  这个例子到这里就完了,这个例子中只是简单的采用了Specifiction模式。但是实际的情况往往是没有这个简单的,因为一个业务逻辑往往要组合多个多个业务规则。下面我们就来进一步的看:如果采用链式的结构来完成复杂的业务逻辑。

 

  Composite Pattern(组合模式)

  :这个模式不属于架构模式,而且GOF模式的一种,这里列出来主要是为了配合之前的Specification模式的,大家不要在这里纠结这个问题 J

 

  Composite模式允许把一个集合对象当做单个的对象来使用,而且我们还可以在这个所谓的单个对象中不断的嵌套。采用这种模式,可以把对象的层级关系组合成为“树形”的结构!我个人喜欢把它称为“容器模式”。

 

  其实这个模式在我们在平时的ASP.NET或者WinForm ,WPF中到处可见。例如一个Panel控件,可以在里面加入另一个Panel,然后在Panel中可以加入GroupBox,然后再GroupBox中还可以加入Button等控件。这就是.NET Framework设计中采用了Compiste模式的例子。

 

  下面来看看Compiste模式的UML结构图:

  

 

  

  在上面的图中:


  1. Component
是一个抽象类,这个类提供了一个 Add 方法,这个 Add 可以加入其他的 Component. 大家想想,这样是否就可以很容易的实现链式的效果。

  2. Leaf就是一个继承Component的具体类。

 

看到上面图,其实大家也可以想想在ASP.NET页面的生命周期中到处都是这种例子:例如在ASP.NET页面的Init事件中,因为Page本身就是一个容器,这个容器里面包含了很多的其他的控件,如Panel,Button,而且Panel里面还是控件。那么在Init方法就会调用自己的子容器的Init方法,然后子容器在调用自己的子容器的Init方法,这样就层层调用,直到最后调用到某个控件的Init的方法。这样这个页面的初始化就完成了。和上面的UML的结构是一样的。

 

下面我们还是来看一个例子吧。继续之前的Specification模式的讨论,看看如果结合则两种模式来组织复杂的业务逻辑。

为了使得例子有点说服力,我们把之前的业务稍微的变复杂一点点:为了判定一个用户是否可以租DVD,我们要进行一系列的规则判定之后才能决定结果:

1.    用户的账号是否处于激活的状态

2.    用户之前是否还欠费

3.    用户租赁DVD的数量是否达到了规定的数量

  下面首先总体来看看一些类图的结构:

  

 

 

不知道大家有没有注意一点:每次我在讲述一个功能的时候,总是先让大家看看总体的类图的设计,然后再开始一个个的讲述。其实这样做事有原因的。在之前的文章中,一直提到“设计Design”。就是说在做一个功能之前,不是一下子就砸进去编码而是首先把功能考虑清楚,然后从总体上考虑功能如何实现,然后写出一些测试代码,最后写出一些实现代码的骨架。上面的类图其实就是一个骨架。

 按照之前的Specification模式的例子,我们首先条件两个类来新增的封装业务规则:

 

    现在我们将例子进行扩充:为了判定一个用户是否可以租DVD,我们要进行一系列的规则判定:

用户的账号是否处于激活的状态。

用户之前是否还欠费。

用户租赁DVD的数量是否达到了规定的数量。

ExpandedBlockStart.gif 代码
public   class  CustomerAccountStillActiveSpecification : ISpecification < CustomerAccount >   
    {
        
public   override   bool  IsSatisfiedBy(CustomerAccount candidate)
        {
            
return  candidate.AccountActive;
        }
    }

  

上面的代码用来判断用户是否处于激活状态

 

ExpandedBlockStart.gif 代码
     public   class  CustomerAccountHasLateFeesSpecification : ISpecification < CustomerAccount >   
    {
        
public   override   bool  IsSatisfiedBy(CustomerAccount candidate)
        {
            
return  candidate.LateFees  >   0 ;
        }
    }

 

上面的代码就判断用户是否欠费 

添加完了所有的业务规则之后,好戏就开始了。

我们要把这些业务规则组合起来,放在容器中,然后只要调用父容器的一个方法,规则验证就一层层进行下去,就像我们之前举的ASP.NETInit事件一样。

 

首先我们来添加一个表示容器的类:

 

ExpandedBlockStart.gif 代码
   public   abstract   class  CompositeSpecification < T >  : ISpecification < T >
    {
        
public   abstract   bool  IsSatisfiedBy(T candidate);

        
public  ISpecification < T >  And(ISpecification < T >  other)
        {
            
return   new  AndSpecification < T > ( this , other);
        }
      
        
public  ISpecification < T >  Not()
        {
            
return   new  NotSpecification < T > ( this );
        }
    }

  

上面的代码有些不明白的地方,没什么,咱们耐心的往下面走。 

 

ExpandedBlockStart.gif 代码
public   class  AndSpecification < T >  : CompositeSpecification < T >
    {
        
private  ISpecification < T >  _leftSpecification;
        
private  ISpecification < T >  _rightSpecification;

        
public  AndSpecification(ISpecification < T >  leftSpecification, ISpecification < T >  rightSpecification)
        {
            _leftSpecification 
=  leftSpecification;
            _rightSpecification 
=  rightSpecification;
        }

        
public   override   bool  IsSatisfiedBy(T candidate)
        {
            
return  _leftSpecification.IsSatisfiedBy(candidate)  &&  _rightSpecification.IsSatisfiedBy(candidate);
        }
    }

 

ExpandedBlockStart.gif 代码
  public   class  NotSpecification < T >  : CompositeSpecification < T >
    {
        
private  ISpecification < T >  _innerSpecification;

        
public  NotSpecification(ISpecification < T >  innerSpecification)
        {
            _innerSpecification 
=  innerSpecification;
        }

        
public   override   bool  IsSatisfiedBy(T candidate)
        {
            
return   ! _innerSpecification.IsSatisfiedBy(candidate);
        }
    }

  

上面基础代码完成了,我们就开始实现我们想要的链式的效果!

我们修改之前的几个规则,和接口的定义,如下:

 

ExpandedBlockStart.gif 代码
public   class  HasReachedRentalThresholdSpecification :CompositeSpecification < CustomerAccount >
{
        …
}

public   class  CustomerAccountStillActiveSpecification :CompositeSpecification < CustomerAccount >
{
       …
}

public   class  CustomerAccountHasLateFeesSpecification :CompositeSpecification < CustomerAccount >
{
      …
}

  

漫长的过程终于结束了,到了核心的部分,请看业务类现在的定义:

 

public  class Customer
{
          private ISpecification<Customer> hasReachedRentalThreshold;
         private ISpecification<Customer> customerIsActive;
         private ISpecification<Customer> customerHasLateFees;

         public Customer()
        {
          hasReachedRentalThreshold =  new HasReachedMaxSpecification();
            customerIsActive =  new CustomerAvtiveSpecification();
            customerHasLateFees =  new CustomerHasLateFeesSpecification();
        }

         public  decimal TotalRentNumber {  getset; }
         public  bool IsActive {  getset; }
         public  decimal LateFees {  getset; }

         public  bool CanRent()
        {
            ISpecification<Customer>canRent =customerIsActive.And(hasReachedRentalThreshold.Not()).And(customerHasLateFees.Not());
             return canRent.IsSatisfiedBy( this);
        }
}

大家主要看看那个 CanRent方法

下面我们就来讲讲这个方法。

customerAccountActive继承自CompositeSpecification,Add方法的定义如下:

 

public  ISpecification < T >  And(ISpecification < T >  other)
{
     
return   new  AndSpecification < T > ( this , other);
}

  

  _customerAccountIsActive.And(_hasReachedRentalThreshold.Not()) 的结果就是使得 customerAccountIsActive 内部包含了平行的两条业务规则,结构如下:

 

 

 

  方法返回的结果还是一个实现了ISpecification的对象,只不过这个对象(我们称之为“容器A”)里面有两个规则了。

  然后这个保量两个业务规则的对象(容器A)再次调用Add方法,如下:

 

  _customerAccountIsActive.And(_hasReachedRentalThreshold.Not()).
                              And(_customerAccountHasLateFees.Not());

 

  此时相当于把之前那个容器 A 作为一个单独对象,再次调用 Add 方法,于是这个三个规则组合成为一个大的规则的容器:如下。

 

 

  今天就到这里,东西不多,大家多琢磨一下!

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值