走向.NET架构设计—第三章—分层设计,初涉架构(中篇)

简介:
走向.NET架构设计—第三章—分层设计,初涉架构(中篇) 
  前言:自从上篇发布以后,大家反馈了不少问题,因为前篇讲的东西不是很深,可能大家看完之后没有什么感觉. 本章(前篇,中篇,后篇)的主要目的其实首先是提出不好的设计,然后对比的提出一个相对比较合理的分层架构,同时本篇也为后续讲述架构模式和设计模式等的文章做个铺垫。
 
本篇的议题如下:
1.  阐明示例需求
2.  业务层设计
3.  服务层设计
4.  数据访问层设计
5.  显示层设计
6.  UI 层设计
    
  1.  阐明示例需求
  本篇还是用之前的电子商务网站中的一个简单的场景来讲述:在页面上需要显示产品的列表信息。并且根据产品的类型不同,计算出相应的折扣。 
  在上篇中,我们已经设计项目的逻辑分层。我们再来回顾下:
可能有的朋友认为从Smart UI 立刻跳到这种分层设计,似乎快了些。其实也算是一个思想的跳跃吧。下面就来看看这种分层是如何解决之前Smart UI 的问题的。 
 
  2.    业务层设计
  记得在之前的 Smart UI 的例子中,程序的业务逻辑是直接写在了 ASPX 页面后面的 cs 代码中的。现在,采用分层的方法,我们采用了领域模型来组织来电子商务中的业务逻辑。
  有关领域模型的一些东西,我们在后续的文章中会讲解的。
  注:领域模型模式被设计用来组织复杂的业务逻辑和关系。
 
  下面的类图就反映了我们之前的电子商务的需求中所用到的业务模型。
  Product 类就代表了电子商务中的每一个产品。
  Price 类将会包含可算折扣的业务逻辑,并且用策略模式来具体实现折扣的算法-
  在Model 添加一个接口类:IDiscountStrategy:
  
public   interface  IDiscountStrategy
{
        
decimal  ApplyExtraDiscountsTo( decimal  OriginalSalePrice);
}
 
 
 
  这个接口就用来实现不同打折的策略,这是策略模式的一种应用。这个模式允许我们在运行的时候更改不同的算法实现。在本例子中,Price 类将会根据不同的产品来实现不同的打折策略。在我们之前的那个Smart UI 例子中,其实这个打折的算法我们已经写了,但是没有分离出来,导致了每次加一个打折的算法的策略,程序就需要改动,重新编译,部署。也就是说打折的部分是个变化点,我们应该分离出来的。 
注:策略模式:用一个类来封装一个算法的实现,并且通过切换算法的实现允许在运行时修改一个对象的行为。
 
在电子商务中,不是每种商品都会打折的,其实我们要实现的打折策略只有一种。但是如果这样,我们在写代码的时候就要if-else 判断是否是打折的商品,其实这里还是暴露了变化点的:如果国庆那天,所有的商品都打折了,那么我们就得修改代码。其实我们可以这样想想:不打折的情况也算是一种打折,其他的商品打折可能是7 折,不打折的情况就是10 折。 
 
代码
  public   class  TradeDiscountStrategy : IDiscountStrategy 
{        
        
public   decimal  ApplyExtraDiscountsTo( decimal  OriginalSalePrice)
        {
            
decimal  price  =  OriginalSalePrice;            
            
            price 
=  price  *   0.6M ;            

            
return  price;
        }     
}
public class NullDiscountStrategy : IDiscountStrategy 
{        
        public decimal ApplyExtraDiscountsTo(decimal OriginalSalePrice)
        {
            return OriginalSalePrice;
        }
}
 
 
下面我们来看看Price 类的实现。
 
代码
public   class  Price
{
        
private  IDiscountStrategy _discountStrategy  =   new  NullDiscountStrategy(); 
        
private   decimal  _rrp;
        
private   decimal  _sellingPrice;

        
public  Price( decimal  RRP,  decimal  SellingPrice)
        {
            _rrp 
=  RRP;
            _sellingPrice 
=  SellingPrice;
        }

        
public   void  SetDiscountStrategyTo(IDiscountStrategy DiscountStrategy)
        {
            _discountStrategy 
=  DiscountStrategy; 
        }

        
public   decimal  SellingPrice
        {
            
get  {  return  _discountStrategy.ApplyExtraDiscountsTo(_sellingPrice); }
        }

        
public   decimal  RRP
        {
            
get  {  return  _rrp; }
        }

        
public   decimal  Discount
        {
            
get  { 
                
if  (RRP  >  SellingPrice) 
                    
return  (RRP  -  SellingPrice); 
                
else
                    
return   0 ;}
        }

        
public   decimal  Savings
        {
            
get {
                
if  (RRP  >  SellingPrice)
                    
return   1   -  (SellingPrice  /  RRP);
                
else
                    
return   0 ;}
        }        
}
  
  Price 类在设计中就是用了“依赖倒置原则”,因为它没有采用某一个具体的打折实现算法,而且依赖于接口抽象,至于之后到底会哪种的打折算法,其实是由商品的类型来决定的。 
  我们还是继续的看,现在看看Product 类。
  
public   class  Product
{
        
public   int  Id {  get set ; }
        
public   string  Name {  get set ; }
        
public  Price Price {  get set ; }
}
 
        
          现在所有的业务实体就已经创建了。至于对商品是否打折,其实这是由客户代码来决定:根据客户代码传入的商品的类型不同,然后调用不同的策略,选择了不同的打折算法计算折扣。所以我们这里来添加一个表示商品类型的枚举:  
  public   enum  CustomerType
 {
        Standard 
=   0 ,
        Trade 
=   1
 }
 
 
  我们将会把选择哪种打折的策略的逻辑写在一个单独的地方,也就是说:只要客户代码传入相应的参数信息,我们就自动的创建一个合适的打折策略对象。很明显,这里可以采用工厂方法来实现,如下:  
 
代码
public   static   class  DiscountFactory
{
        
public   static  IDiscountStrategy GetDiscountStrategyFor(CustomerType customerType)
        {
            
switch  (customerType)
            {
                
case  CustomerType.Trade:
                    
return   new  TradeDiscountStrategy(); 
                
default :
                    
return   new  NullDiscountStrategy(); 
            }
        }
}
 
 
  在上面的逻辑分层中,我们建立了一个Repository 的类库,其实我们就是想采用Repository 模式来实现持久化无关性”----- 业务类完全不用管如何保存和获取数据。而且由Repository 决定数据的来源和保存的地方,可能是数据库,也可能就是内存,但是不管怎么,业务类是不用管这些的。所以下面用一个接口来实现灵活性:  
 
  public   interface  IProductRepository
 {
        IList
< Product >  FindAll();
 }
 
 
  如果现在有很多的商品,我们想知道他们的折扣价格,最简单的方法就是遍历他们,判断类型,然后应用不同的打折策略。为了更加的可读,我们可以为商品列表建立扩展方法,如下:
  
代码
  public   static   class  ProductListExtensionMethods
 {
      
public   static   void  Apply( this  IList < Product >  products, IDiscountStrategy discountStrategy)
      {
            
foreach  (Product p  in  products)
            {
                p.Price.SetDiscountStrategyTo(discountStrategy);
            }
      }
 }
 
 
  为了简化客户代码的调用工作,我们提供一个类似门户(gateway) ,或者是Façade 的概念:把复杂的操作逻辑隐藏,留给客户代码一个简单易用的API 。我们这里创建一个Service 类,如下:
  
代码
public   class  ProductService
{
   
private  IProductRepository _productRepository;

   
public  ProductService(IProductRepository productRepository)
   {
         _productRepository 
=  productRepository;
   }

   
public  IList < Product >  GetAllProductsFor(CustomerType customerType)
   {
      IDiscountStrategy discountStrategy 
=  DiscountFactory.GetDiscountStrategyFor(customerType);
      IList
< Product >  products  =  _productRepository.FindAll();
      products.Apply(discountStrategy);
      
return  products;
    }    
}
 
 
  只要客户代码(如显示层中的代码)直接调用上面的方法就可以了,而且商品的折扣也根据传入的商品类型不同来计算。
 
  3.        服务层设计
  服务层就充当应用程序的入口的角色。有时候,可以被认为是façade. 不仅如此,因为service 分为领域逻辑的service 和门户的service 。门户的service 常常为显示层提供强类型的View Model (有时也称为Presentation Model )。  一个View Model 就是给一个专门的View 来使用的。在本例中,我们将会建立Product View Model 来显示商品的信息。一般情况下,我们不要把业务类直接暴露给显示层,这样很容易紧耦合,所以在中间就上一个View Model ,其实View Model 和业务类的结构差不多,只是View Model 做了一些调整,便于最后的显示。关于View Model 详细的,后文讲述。
  注: Façade 模式:为内部负责的子系统提供一个简单的接口供外部访问。
 
  下面我们就来看看Product View Model 是如何写的:
 
代码
public   class  ProductViewModel
    {
        
public   int  ProductId {  get set ; }
        
public   string  Name {  get set ; }
        
public   string  RRP {  get set ; }
        
public   string  SellingPrice {  get set ; }
        
public   string  Discount {  get set ; }
        
public   string  Savings {  get set ; }
    }
 
 
  可以看到,其实View Model 就是做了一些显示逻辑的处理。在这里就是多加了一些字段,这些字段就是在UI GridView 中显示用的。我们之前的Smart UI 的方法中,还建立了模板列来显示Product 类中没有的字段,其实就相当于在UI 中作了一定的显示逻辑的处理。这里我们直接显示ViewModel.
 
  大家应该很熟悉Web Service :在客户端和服务使用请求/ 响应的消息机制进行通信的。我们这里的客户代码和Service 也采用这种方法,因为很有可能我们在部署的时候Service 的代码和客户代码(显示层)在不同机器上。
  请求的消息的结构如下:  
 
  public   class  ProductListRequest
 {
        
public  CustomerType CustomerType {  get set ; }
 }
 
 
  服务在响应请求的时候也要定义格式,而且我们可以在响应中加入更多的属性来判断这个请求是否成功。所以在下面的代码中,我们加入了Message 属性,用来在请求失败的时候显示错误信息,还添加了一个Success 属性用来判断请求的状态:  
 

public   class  ProductListResponse
{
   
public   bool  Success {  get set ; }
   
public   string  Message {  get set ; }
   
public  IList < ProductViewModel >  Products {  get set ; }
}
 
 
  还有一点不要忘记了:因为Product 和它对应的View Model 结构不同的,而Service 返回的又是ViewModel 的响应,那么就需要把获取到的Product 转换为View Model 的结构。可以把转换的代码写在一个特定的地方(可以认为是个Mapping的过程),为了阅读的方便,我们可以为List<Product> 添加扩展方法,直接调用,如下:
  
 
代码
public   static   class  ProductMapperExtensionMethods
    {
        
public   static  IList < ProductViewModel >  ConvertToProductListViewModel( this  IList < Model.Product >  products)
        {
            IList
< ProductViewModel >  productViewModels  =   new  List < ProductViewModel > ();

            
foreach (Model.Product p  in  products)
            {
                productViewModels.Add(p.ConvertToProductViewModel());  
            }

            
return  productViewModels;
        }

        
public   static  ProductViewModel ConvertToProductViewModel( this  Model.Product product)
        { 
            ProductViewModel productViewModel 
=   new  ProductViewModel();
            productViewModel.ProductId 
=  product.Id;
            productViewModel.Name 
=  product.Name;
            productViewModel.RRP 
= product.Price.RRP;
            productViewModel.SellingPrice 
= product.Price.SellingPrice;
            
            
if  (product.Price.Discount  >   0 )
                productViewModel.Discount 
=   product.Price.Discount;

            
if  (product.Price.Savings  <   1   &&  product.Price.Savings  >   0 )
                productViewModel.Savings 
=  product.Price.Savings.ToString( " #% " );

            
return  productViewModel;
        }
    }
 
 
  最后,我们加入一个ProductService 来与业务层的Service  类进行交互,业务层的Service 会返回商品列表,然后我们现在添加的这个ProductService 会把列表转为ProductViewModels
 
大家可能觉得奇怪:为什么这里添加了两个ProductService, 之前在业务层加一个,现在又加一个,是否命名有问题或者功能重复?其实在上一篇已经提过:有时在业务层类添加一个service 层,主要是用来组织业务流程的,常常要几个业务类组合在一起使用,这样主要是为了简化客户程序(也就是调用这个业务层的代码)的调用,实现类似Façade 的作用。
 
我们现在添加的ProductService 就是业务层中 service 的客户程序,因为我们调用了业务层的service ,往往有时候,我们不想把自己系统的业务类的结构直接暴露给外界,如显示层,而且也希望提供更加符合显示层所需的数据结构,那么我们就添加了这个ProductService ,提供从业务类到ViewModel 的转换。而且在这个ProductSevice 中,我们也可以实现一些异常处理机制,如果涉及到了分布式调用,那么我们还可以用这个ProductService 类向显示层和UI 那边隐藏分布式的信息:实现代理模式。
今天就写在到这里,在写的过程中发现这篇有点长了,所以分为3篇(前,中,后)发布!不明白的地方大家多琢磨一下,也可以告诉我!下篇明天发布!见谅!



















本文转自yanyangtian51CTO博客,原文链接: http://blog.51cto.com/yanyangtian/411766  ,如需转载请自行联系原作者


相关文章
|
16天前
|
人工智能 前端开发 JavaScript
前端架构思考 :专注于多框架的并存可能并不是唯一的方向 — 探讨大模型时代前端的分层式微前端架构
随着前端技术的发展,微前端架构成为应对复杂大型应用的流行方案,允许多个团队使用不同技术栈并将其模块化集成。然而,这种设计在高交互性需求的应用中存在局限,如音视频处理、AI集成等。本文探讨了传统微前端架构的不足,并提出了一种新的分层式微前端架构,通过展示层与业务层的分离及基于功能的横向拆分,以更好地适应现代前端需求。
|
1月前
|
JSON 前端开发 Java
Spring Boot框架中的响应与分层解耦架构
在Spring Boot框架中,响应与分层解耦架构是两个核心概念,它们共同促进了应用程序的高效性、可维护性和可扩展性。
45 3
|
28天前
|
消息中间件 运维 数据库
架构设计之解析CQRS架构模式!
架构设计之解析CQRS架构模式!
架构设计之解析CQRS架构模式!
|
1月前
|
存储 前端开发 API
DDD领域驱动设计实战-分层架构
DDD分层架构通过明确各层职责及交互规则,有效降低了层间依赖。其基本原则是每层仅与下方层耦合,分为严格和松散两种形式。架构演进包括传统四层架构与改良版四层架构,后者采用依赖反转设计原则优化基础设施层位置。各层职责分明:用户接口层处理显示与请求;应用层负责服务编排与组合;领域层实现业务逻辑;基础层提供技术基础服务。通过合理设计聚合与依赖关系,DDD支持微服务架构灵活演进,提升系统适应性和可维护性。
|
26天前
|
存储 消息中间件 前端开发
.NET常见的几种项目架构模式,你知道几种?
.NET常见的几种项目架构模式,你知道几种?
|
3月前
|
设计模式 存储 前端开发
揭秘.NET架构设计模式:如何构建坚不可摧的系统?掌握这些,让你的项目无懈可击!
【8月更文挑战第28天】在软件开发中,设计模式是解决常见问题的经典方案,助力构建可维护、可扩展的系统。本文探讨了.NET中三种关键架构设计模式:MVC、依赖注入与仓储模式,并提供了示例代码。MVC通过模型、视图和控制器分离关注点;依赖注入则通过外部管理组件依赖提升复用性和可测性;仓储模式则统一数据访问接口,分离数据逻辑与业务逻辑。掌握这些模式有助于开发者优化系统架构,提升软件质量。
52 5
|
3月前
|
XML 开发框架 .NET
.NET框架:软件开发领域的瑞士军刀,如何让初学者变身代码艺术家——从基础架构到独特优势,一篇不可错过的深度解读。
【8月更文挑战第28天】.NET框架是由微软推出的统一开发平台,支持多种编程语言,简化应用程序的开发与部署。其核心组件包括公共语言运行库(CLR)和类库(FCL)。CLR负责内存管理、线程管理和异常处理等任务,确保代码稳定运行;FCL则提供了丰富的类和接口,涵盖网络、数据访问、安全性等多个领域,提高开发效率。此外,.NET框架还支持跨语言互操作,允许开发者使用C#、VB.NET等语言编写代码并无缝集成。这一框架凭借其强大的功能和广泛的社区支持,已成为软件开发领域的重要工具,适合初学者深入学习以奠定职业生涯基础。
99 1
|
3月前
|
监控 安全 中间件
Python Django 后端架构开发: 中间件架构设计
Python Django 后端架构开发: 中间件架构设计
38 1
|
3月前
|
存储 消息中间件 JSON
|
3月前
|
存储 缓存 Cloud Native
MPP架构数据仓库使用问题之ADB PG相比Greenplum的HAWQ在架构设计上有什么不同
MPP架构数据仓库使用问题之ADB PG相比Greenplum的HAWQ在架构设计上有什么不同

热门文章

最新文章