《.NET应用架构设计:原则、模式与实践》新书博客--试读-2.1.2 设计原则实战

简介:





2.1.2  设计原则实战

 
    下面我们就以一个简单的电子商务系统为背景:通过给定的产品分类ID获取该分类下的所有产品。
对于这个问题,基本上不用想就可以实现,如图2-1的类图设计。
 
 
2-1  获取分类产品的类图
 
其中:
q  ProductService类被客户程序调用,通过调用GetAllProductsFrom方法来获取产品,并且此处还可以添加缓存策略或异常处理等机制。
 
q  Product类代表了产品的实体。
 
q  ProductRepository类用来从数据存储设备(数据库或XML等其他数据存储器)中读取产品信息。     
 
注意  为了减少读者不必要的麻烦,让理解更加直观,以后的章节中,都会给出完整的代码示例和项目图示。
在这个示例中,Visual Studio项目的结构图如图2-2所示。
 
2-2  Visual Studio解决方案图
 
 
    正如之前所说:ProductService提供外部调用的方法接口,并且采用了缓存策略和异常处理机制,来提高程序的性能和健壮性,代码如下所示:
 
using System; 
 
usingSystem.Collections.Generic; 
 
usingSystem.Web; 
 
using AgileSharp.Chapter2.Repository; 
 
using AgileSharp.Chapter2.Domain; 
 
 
 
namespace AgileSharp.Chapter2.Service 
 

 
         public  class ProductService 
 
        { 
 
         private ProductRepository productRepository =  null
 
 
 
         public ProductService() 
 
                { 
 
                productRepository =  new ProductRepository(); 
 
                } 
 
 
 
         public List<Product> GetAllProductsFrom(intcategoryId) 
 
                { 
 
                        List<Product> result =  new List<Product>(); 
 
                 try 
 
                        { 
 
                                 string cacheKey =  string.Format( "products_in_category_{0}",categoryId); 
 
                result = HttpContext.Current.Cache.Get(cacheKey)  as List<Product>; 
 
 
 
                 if (result ==  null
 
                                { 
 
                        result = productRepository.GetProductsFrom(categoryId); 
 
                         if (result !=  null &&result.Count> 0) 
 
                                            { 
 
                        HttpContext.Current.Cache.Insert(cacheKey, result); 
 
                                            } 
 
                                } 
 
                        } 
 
                 catch (Exception ex) 
 
                        { 
 
                                 //Log will add here later 
 
 
                        } 
 
                 return result; 
 
                } 
 
        } 
 

 
    至于 Product ProductRepository 的代码就非常简单了,这里不多解释。
 
示例到这里,也许大家有个疑问:上面的代码有些什么问题?
 
q   ProductService 依赖于 ProductRepository 。在没有任何变化的情况下,这没有什么问题。但是如果现在项目换了数据存储设备,例如将数据库换成了 XML 文件,或者数据库的访问技术从 ADO.NET  换成了 Entity Framework ,那么 ProductRepository 的代码就得改变,这会使得整个项目都需要重新编译,重新部署。问题来了:此时系统中只有 ProductRepository 一个变化点,为什么非得要求整个项目重新编译,重新部署呢?难道不能只重新编译和部署那个变化的模块呢?
 
q   代码不具有测试性能。要想知道此段功能代码是否按照了我们的意愿运行,可以通过人工审核,然后通过 GUI 界面获取数据来进行调试,此时的逻辑相对而言比较简单,此方法也还行得通。不过,一旦业务逻辑变得复杂或代码量的剧增,那么很难确保代码不会出错,而这些错误很多时候只会在运行时才能被发现。
 
q   缓存机制依赖于 HttpContext ,这不仅仅会让测试产生困难(尽管可以有 Mock ),而且会对后续系统的扩展有阻碍(例如采用分布式缓存)。
 
对以上的问题进行进一步分析,可以知道,这都是因为违背了以下设计原则:
 
q   ProductService 依赖了 ProductRepository 的具体实现,而 ProductRepository 是一个可能的变化点。也就是说: ProductService 这个高层模块依赖了 ProductRepository 底层模块,违背了依赖倒置原则,这也就使得一个 ProductRepository 变化,整个项目都需要重新编译,重新部署。同理,缓存机制也是。
 
q   对于可测试性的问题,严格来说,上面的代码是可以测试的,但是测试的时候必须依赖于外部的数据存储设备,例如数据库,那么测试的结果可能会由于数据的变动而不一样,而且每次测试所花的时间也会比较长。
 
接下来尝试重构上面的代码,让代码的组织方式更加的灵活和易于扩展。
 
 既然上面代码主要是违背了 DIP依赖倒置原则(再次回顾一下DIP:依赖抽象,而不依赖具体实现)。
 
    那么现在就提出接口 IProductRepository ,使得 ProductService 依赖这个接口,代码如下:
 
 
using System.Collections.Generic; 
 
using AgileSharp.Chapter2.Domain; 
 
 
 
namespace AgileSharp.Chapter2.Repository 

 
         public  interface IProductRepository 
        { 
 
                List<Product> GetProductsFrom(intcategoryId);    
 
        } 

   
    现在接口已经抽象出来了,ProductService可以直接依赖接口了,但是我们现在还需要一个实现IProductRepository接口的类ProductRepository,然后再采用LSP(里氏替换原则),用ProductRepository替换。代码如下:
 
using System; 
usingSystem.Collections.Generic; 
using AgileSharp.Chapter2.Domain; 
 
namespace AgileSharp.Chapter2.Repository 

 
         public  class ProductRepository:IProductRepository 
        { 
 
          //… 
 
        } 

    现在 ProductService 的代码如下所示:
 
using System; 
usingSystem.Collections.Generic; 
usingSystem.Web; 
using AgileSharp.Chapter2.Repository; 
using AgileSharp.Chapter2.Domain; 
 
namespace AgileSharp.Chapter2.Service 

 
         public  class ProductService 
        { 
 
        privateIProductRepositoryproductRepository =  null
 
 
        publicProductService() 
                { 
 
                productRepository =  new ProductRepository(); 
 
                } 
 
         public List<Product> GetAllProductsFrom(intcategoryId) 
             { 
 
                      //... 
 
             } 
        } 
 

    其实可以看到,上面 ProductService 的代码虽然提出了抽象接口,但问题依然存在:仍依赖于 ProductRepository 的具体实现。
 
    问题到这里就好办了,可以采用工厂模式,通过读取配置文件进行反射或采用依赖注入等方法得到解耦。
 
    可以看出:设计原则解决了变化点的问题,将 ProductRepository 这个变化点从 ProductService 中移出,然后一步步的迁移,最后把这个变化点引到了配置文件中,也就是将变化点引到了系统之外,也许这些正是我们需要的。
 
    京东地址: http://book.360buy.com/10893935.html
    卓越地址: http://www.amazon.cn/mn/dp/B006NS2N0S





















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




相关文章
|
2月前
|
存储 Shell Linux
快速上手基于 BaGet 的脚本自动化构建 .net 应用打包
本文介绍了如何使用脚本自动化构建 `.net` 应用的 `nuget` 包并推送到指定服务仓库。首先概述了 `BaGet`——一个开源、轻量级且高性能的 `NuGet` 服务器,支持多种存储后端及配置选项。接着详细描述了 `BaGet` 的安装、配置及使用方法,并提供了 `PowerShell` 和 `Bash` 脚本实例,用于自动化推送 `.nupkg` 文件。最后总结了 `BaGet` 的优势及其在实际部署中的便捷性。
125 10
|
7天前
|
开发框架 监控 .NET
【Azure App Service】部署在App Service上的.NET应用内存消耗不能超过2GB的情况分析
x64 dotnet runtime is not installed on the app service by default. Since we had the app service running in x64, it was proxying the request to a 32 bit dotnet process which was throwing an OutOfMemoryException with requests >100MB. It worked on the IaaS servers because we had the x64 runtime install
|
15天前
|
运维 NoSQL Java
后端架构演进:微服务架构的优缺点与实战案例分析
【10月更文挑战第28天】本文探讨了微服务架构与单体架构的优缺点,并通过实战案例分析了微服务架构在实际应用中的表现。微服务架构具有高内聚、低耦合、独立部署等优势,但也面临分布式系统的复杂性和较高的运维成本。通过某电商平台的实际案例,展示了微服务架构在提升系统性能和团队协作效率方面的显著效果,同时也指出了其带来的挑战。
55 4
|
20天前
|
JSON 算法 安全
JWT Bearer 认证在 .NET Core 中的应用
【10月更文挑战第30天】JWT(JSON Web Token)是一种开放标准,用于在各方之间安全传输信息。它由头部、载荷和签名三部分组成,用于在用户和服务器之间传递声明。JWT Bearer 认证是一种基于令牌的认证方式,客户端在请求头中包含 JWT 令牌,服务器验证令牌的有效性后授权用户访问资源。在 .NET Core 中,通过安装 `Microsoft.AspNetCore.Authentication.JwtBearer` 包并配置认证服务,可以实现 JWT Bearer 认证。具体步骤包括安装 NuGet 包、配置认证服务、启用认证中间件、生成 JWT 令牌以及在控制器中使用认证信息
|
7天前
|
消息中间件 开发框架 .NET
.NET 8 强大功能 IHostedService 与 BackgroundService 实战
【11月更文挑战第7天】本文介绍了 ASP.NET Core 中的 `IHostedService` 和 `BackgroundService` 接口及其用途。`IHostedService` 定义了 `StartAsync` 和 `StopAsync` 方法,用于在应用启动和停止时执行异步操作,适用于资源初始化和清理等任务。`BackgroundService` 是 `IHostedService` 的抽象实现,简化了后台任务的编写,通过 `ExecuteAsync` 方法实现长时间运行的任务逻辑。文章还提供了创建和注册这两个服务的实战步骤,帮助开发者在实际项目中应用这些功能。
|
1月前
|
开发框架 NoSQL MongoDB
C#/.NET/.NET Core开发实战教程集合
C#/.NET/.NET Core开发实战教程集合
|
1月前
|
存储 前端开发 API
DDD领域驱动设计实战-分层架构
DDD分层架构通过明确各层职责及交互规则,有效降低了层间依赖。其基本原则是每层仅与下方层耦合,分为严格和松散两种形式。架构演进包括传统四层架构与改良版四层架构,后者采用依赖反转设计原则优化基础设施层位置。各层职责分明:用户接口层处理显示与请求;应用层负责服务编排与组合;领域层实现业务逻辑;基础层提供技术基础服务。通过合理设计聚合与依赖关系,DDD支持微服务架构灵活演进,提升系统适应性和可维护性。
|
1月前
|
网络协议 大数据 网络架构
桥接模式和NET模式的区别
桥接模式和NET模式的区别
35 0
|
2月前
|
运维 持续交付 API
深入理解并实践微服务架构:从理论到实战
深入理解并实践微服务架构:从理论到实战
133 3
|
2月前
|
存储 缓存 负载均衡
亿级流量架构理论+秒杀实战系列(二)
亿级流量架构理论+秒杀实战系列(二)