应用程序分层设计

应用程序分层属于关注点分离的一种形式,可以通过命名空间、文件夹或采用单独的项目来实现。



下图为一个采用分层设计的项目结构

  • ASPPatterns.Chap3.Layered.Repository依赖于ASPPatterns.Chap3.Layered.Model
  • ASPPatterns.Chap3.Layered.Service依赖于ASPPatterns.Chap3.Layered.Repository和ASPPatterns.Chap3.Layered.Model
  • ASPPatterns.Chap3.Layered.Presentation依赖于ASPPatterns.Chap3.Layered.Service和ASPPatterns.Chap3.Layered.Model
  • ASPPatterns.Chap3.Layered.ConsoleApp依赖于ASPPatterns.Chap3.Layered.Repository、ASPPatterns.Chap3.Layered.Model、ASPPatterns.Chap3.Layered.Service和ASPPatterns.Chap3.Layered.Presentation

业务层

通过创建一个领域模型来存放所有正在建模的对象的业务有关的行为和数据。

//示例:商品折扣业务处理
namespace ASPPatterns.Chap3.Layered.Model
{
/// <summary>
/// 折扣接口
/// 通过策略模式,可以在运行时选择和改变算法(折扣)
/// </summary>
public interface IDiscountStrategy
{
decimal ApplyExtraDiscountsTo(decimal OriginalSalePrice);
} /// <summary>
/// 商品折扣策略-打88折
/// </summary>
public class TradeDiscountStrategy : IDiscountStrategy
{
public decimal ApplyExtraDiscountsTo(decimal OriginalSalePrice)
{
return OriginalSalePrice * 0.88M;
}
} /// <summary>
/// 无折扣
/// </summary>
public class NullDiscountStrategy : IDiscountStrategy
{
public decimal ApplyExtraDiscountsTo(decimal OriginalSalePrice)
{
return OriginalSalePrice;
}
} /// <summary>
/// 将折扣策略应用到商品的价格
/// </summary>
public class Price
{
private IDiscountStrategy _discountStrategy = new NullDiscountStrategy();
public decimal _rrp;
private decimal _sellingPrice; public Price(decimal rrp, decimal sellingPrice)
{
_rrp = rrp;
_sellingPrice = sellingPrice;
} public decimal RRP { get { return _rrp; } } public decimal SellingPrice
{
get { return _discountStrategy.ApplyExtraDiscountsTo(_sellingPrice); }
} 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;
}
} /// <summary>
/// 通过Set方法注入折扣策略
/// </summary>
public void SetDiscountStrategyTo(IDiscountStrategy discountStrategy)
{
_discountStrategy = discountStrategy;
}
} public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public Price Price { get; set; }
} public enum CustomerType
{
Standard = 0,
Trade
} /// <summary>
/// 折扣策略的选择
/// </summary>
public static class DiscountFactory
{
public static IDiscountStrategy GetDiscountStrategy(CustomerType customerType)
{
switch (customerType)
{
case CustomerType.Trade:
return new TradeDiscountStrategy();
default:
return new NullDiscountStrategy();
}
}
} /// <summary>
/// 服务层与数据存储交互,意见所商品。使用仓储模式来实现该功能,但只是指定资源库接口,
/// 这里因为不希望model层牵涉到诸如使用什么类型的数据存储或使用什么类型的技术来查询等细节
/// </summary>
public interface IProductRepository
{
IList<Product> FindAll();
} /// <summary>
/// 服务类需要能够将给定的折扣策略应用到一组商品。
/// 通过扩展方法可以更灵活的创建一个自定义集合来实现该功能
/// </summary>
public static class ProductListExtensionMenthods
{
public static void Apply(this IList<Product> products, IDiscountStrategy discountStrategy)
{
foreach (var item in products)
{
item.Price.SetDiscountStrategyTo(discountStrategy);
}
}
} /// <summary>
/// 客户端与领域交互的服务类
/// </summary>
public class ProductService
{
private IProductRepository _productRepository;
public ProductService(IProductRepository productRepository)
{
_productRepository = productRepository;
} public IList<Product> GetAllProductsFor(CustomerType customerType)
{
IDiscountStrategy discountStrategy = DiscountFactory.GetDiscountStrategy(customerType);
IList<Product> products = _productRepository.FindAll();
products.Apply(discountStrategy);
return products;
}
}
}

服务层

服务层的作用就是充当应用的入口,为表示层提供了强类型视图模型。所谓视图模型就是平常界面上绑定的实体类。

namespace ASPPatterns.Chap3.Layered.Service
{
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; }
} /// <summary>
/// 为了让客户端与服务层交互,使用Reuqest/Response消息模式
/// Reuqest由客户端提供,它将携带必要的参数,如本示例中的CustomerType
/// </summary>
public class ProductListRequest
{
public CustomerType CustomerType { get; set; }
} /// <summary>
/// 响应客户端请求的内容
/// </summary>
public class ProductListResponse
{
public bool Success { get; set; }
public string Message { get; set; }
public IList<ProductViewModel> Products { get; set; }
} /// <summary>
/// 将Product转换成ProductViewModel
/// </summary>
public static class ProductMapperExtensionMethods
{
public static IList<ProductViewModel> ConvertToProductListViewModel(this IList<Product> products)
{
IList<ProductViewModel> productViewModels = new List<ProductViewModel>();
foreach (var item in products)
{
productViewModels.Add(item.ConvertToProductViewModel());
}
return productViewModels;
} public static ProductViewModel ConvertToProductViewModel(this Product product)
{
ProductViewModel productViewModel = new ProductViewModel();
productViewModel.ProductId = product.Id;
//....其他赋值
return productViewModel;
}
} /// <summary>
/// ProductService与领域模型服务交互,检索商品列表,并将其转换成ProductListViewModel列表
/// </summary>
public class ProductService
{
private Model.ProductService _productService;
public ProductService(Model.ProductService productService)
{
_productService = productService;
} public ProductListResponse GetAllProductsFor(ProductListRequest productListRequest)
{
ProductListResponse productListResponse = new ProductListResponse();
try
{
IList<Product> productEntities = _productService.GetAllProductsFor(productListRequest.CustomerType);
productListResponse.Products = productEntities.ConvertToProductListViewModel();
productListResponse.Success = true;
}
catch (Exception)
{ productListResponse.Success = false;
}
return productListResponse;
}
}
}

数据访问层

主要是操作数据库数据的读写。比较常用的框架有Entity Framework、Hibernate,有时候像Dapper这种轻量级的类库更灵活。

namespace ASPPatterns.Chap3.Layered.Repository
{
public class ProductRepository : IProductRepository
{
public IList<Model.Product> FindAll()
{
//linq to sql return new List<Model.Product>();
}
}
}

表示层

为了将表示逻辑与用户界面分离,一般使用MVP(视图-模型-展示)模式。拥有表示层的好处是可以很容易的测试数据的表示以及系统之间的交互。

namespace ASPPatterns.Chap3.Layered.Presentation
{
public interface IProductListView
{
void Display(IList<ProductViewModel> products);
Model.CustomerType CustomerType { get; }
string ErrorMessage { set; }
} /// <summary>
/// 呈现器负责获取数据、处理用户事件并通过视图的接口更新视图
/// </summary>
public class ProductListPresenter
{
private IProductListView _productListView;
private Service.ProductService _productService;
public ProductListPresenter(IProductListView productListView, Service.ProductService productService)
{
_productListView = productListView;
_productService = productService;
} public void Display()
{
ProductListRequest productListRequest = new ProductListRequest();
productListRequest.CustomerType = _productListView.CustomerType;
ProductListResponse productListResponse = _productService.GetAllProductsFor(productListRequest);
if (productListResponse.Success)
{
_productListView.Display(productListResponse.Products);
}
else
{
_productListView.ErrorMessage = productListResponse.Message;
}
}
}
}

UI(用户体验)层

主要是实现数据的呈现以及与用户的交互。它不需要关注数据是如何从数据库获取的,只要负责处理用户事件并转发调用,将工作委托给表示层的Presenter。

总结



从上面的例子中我们可以看出程序分层设计的好处,应用程序的关注点分解到不同的层次,可以使程序更易于理解和维护。

《ASP.NET 设计模式》

ASP.NET 设计模式:应用程序分层与关注点分离(SoC)的更多相关文章

  1. Asp.net设计模式笔记之二:应用程序分离与关注点分离

    本次笔记主要涉及的内容如下: 1.将智能UI(SmartUI)反模式重构成分层方式的示例代码 2.分层设计与传统的Asp.net WebForm模型(代码后植)相比具有的优势 3.逻辑分层概念以及分离 ...

  2. ASP.NET 设计模式(转)

    Professional ASP.NET Design Patterns 为什么学习设计模式? 运用到ASP.NET应用程序中的设计模式.原则和最佳实践.设计模式和原则支持松散耦合.高内聚的代码,而这 ...

  3. ASP.NET 设计模式:设计模式和原则简述

    设计模式的概念 设计模式是高层次的.抽象的解决方案模板.可以将这些模式视为解决方案的蓝本而不是解决方案本身.通常是通过重构自己的代码并将问题泛化来实现设计模式. 软件设计中常见的模式大体分为三类: 创 ...

  4. 04 入门 - ASP.NET MVC应用程序的结构

    目录索引:<ASP.NET MVC 5 高级编程>学习笔记 用Visual Studio创建了一个新的ASP.NET MVC应用程序后,将自动向这个项目中添加一些文件和目录. 如图所示: ...

  5. (转) 将ASP.NET Core应用程序部署至生产环境中(CentOS7)

    原文链接: http://www.cnblogs.com/ants/p/5732337.html 阅读目录 环境说明 准备你的ASP.NET Core应用程序 安装CentOS7 安装.NET Cor ...

  6. 使用Metrics.NET 构建 ASP.NET MVC 应用程序的性能指标

    通常我们需要监测ASP.NET MVC 或 Web API 的应用程序的性能时,通常采用的是自定义性能计数器,性能计数器会引发无休止的运维问题(损坏的计数器.权限问题等).这篇文章向你介绍一个新的替代 ...

  7. .NET跨平台:在Ubuntu上用自己编译的dnx运行ASP.NET 5示例程序

    在 Linux Ubuntu 上成功编译 dnx 之后,会在 artifacts/build/ 文件夹中生成 dnx-coreclr-linux-x64/ 与 dnx-mono/ 这2个文件夹,前者是 ...

  8. .NET跨平台:在CentOS上编译dnx并运行ASP.NET 5示例程序

    在之前的博文中我们在 Ubuntu 上成功编译出了 dnx ,并且用它成功运行了 ASP.NET 5 示例程序.在这篇博文中我们将 Ubuntu 换成 CentOS. 目前 dnx 的编译需要用到 m ...

  9. ASP.NET Core 中文文档 第二章 指南(1)用 Visual Studio Code 在 macOS 上创建首个 ASP.NET Core 应用程序

    原文:Your First ASP.NET Core Application on a Mac Using Visual Studio Code 作者:Daniel Roth.Steve Smith ...

随机推荐

  1. 错误代码: 1054 Unknown column &#39;t.createUsrId&#39; in &#39;group statement&#39;

    1.错误描写叙述 1 queries executed, 0 success, 1 errors, 0 warnings 查询:select count(t.id),t.`createUserId` ...

  2. Qt---自定义界面之 Style Sheet

    这次讲Qt Style Sheet(QSS),QSS是一种与CSS类似的语言,实际上这两者几乎完全一样.既然谈到CSS我们就有必要说一下盒模型. 1. 盒模型(The Box Model) 在样式中, ...

  3. JAVA入门[2]-安装Maven

    一.资料 1.官网: https://maven.apache.org/ 二.下载Maven 下载地址:https://maven.apache.org/download.cgi# 三.Windows ...

  4. js、jquery实现模糊搜索功能

    模糊搜索功能在工作中应用广泛,并且很实用,自己写了一个方法,以后用到的时候可以直接拿来用了! 实现的搜索功能: 1. 可以匹配输入的字符串找出列表中匹配的项,列表框的高度跟随搜索出的列表项的多少改变 ...

  5. Eclipse Maven构建WebApp项目资源目录显示不全的原因与解决方式

    一.问题展示 1.Eclipse在使用Maven构建WebApp项目的时候,首先Maven的安装和配置都没有问题的,但是构建项目之后,Maven项目要求的几个必须要有的资源目录显示不了: 问题如下图: ...

  6. JaveScript运算符(JS知识点归纳三)

    JaveScript中有许多的运算符,在这里就只说明一些需要注意的. 01 一元运算符 一元:指的是参与运算的操作数只有一个 最经常使用的是++   -- 计算规则: ++/-- 前置于操作数的时候 ...

  7. Python学习日记day10------函数的命名空间、作用域与闭合函数

    1,参数陷阱 如果默认参数的只是一个可变数据类型,那么每一次调用的时候,如果不传值就共用这个数据类型的资源. 2,三元运算 c=a if a>b else b#如果a>b返回a,否则,返回 ...

  8. 深谈auto变量

    1.c++中有一个关键字auto,c语言也有这么一个关键字,但是两者的意义大不相同. 2.c++中用auto定义的变量自动匹配赋值号右边的值的类型,具有自动匹配类型的作用,而c语言中auto只是声明一 ...

  9. Docker -- 安全/部分命令/Daemon

    Docker -- 终极指南 1.安装过程 -- Docker -- docker pull 镜像 -- docker images 列出镜像    -- docker run --rm -ti ub ...

  10. 设计模式之 - 代理模式(Proxy Pattern)

    代理模式:代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问.代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理.很多可以框架中都有用 ...