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

简介:   前言:设计模式并不是什么很高深的东西,至少不是那么“神乎其神”。说到底,设计模式就是一些设计思想。下面我们就走进项目,看看这些项目中这些思想是如何体现的。本系列文章会在后续文章中陆陆续续的,在恰当的时候介绍一些相应的设计模式,而不是一股脑的一起上。

  前言:设计模式并不是什么很高深的东西,至少不是那么“神乎其神”。说到底,设计模式就是一些设计思想。下面我们就走进项目,看看这些项目中这些思想是如何体现的。本系列文章会在后续文章中陆陆续续的,在恰当的时候介绍一些相应的设计模式,而不是一股脑的一起上。

  设计模式

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

  Factory Method

相信很多朋友对这个模式很熟悉了,平时在项目中或多或少总能看到Factory, Provider等。确实Factory Method一种创建型的模式,它的主要目的就是隐藏对象创建的细节。也就是说,客户程序(或者成为调用者)不用特定来什么创建某一种具体的类,也不依赖于特定的类,而且依赖接口或者抽象类,这样就达到了解耦,专业点的说法就是“依赖倒置”,更加直白的说法就是:客户程序可以使用很多不同的实现类,而保持代码不变。因为在需要的时候,传入一些信息,Factory Methods就返回接口或者抽象类的实现类。

         很多情况下,我们一般是这样来使用Factory Method模式的:建立一个Factory类,这个类有一个静态的方法,这个方法返回一个抽象的类或者接口。然后,客户程序(或者调用程序)就传入一些信息给Factory类来,要求Factory来创建相对应,需要的具体的实现类。

         下面我们就看看一个Factory Method的UML图:

  在上面的图中:

  1. Client类通过Factory类得到了一个IProduct接口的实现类。Client只是提供了一些信息,但是不知道具体的类是如何创建的。

  2. Factory类常常基于Client类传入的信息来决定到底创建那个具体类。

  3. IProduct有两个具体的实现者:ConcreteProductA, ConcreteProductB. 

  理论就介绍到这里了,下面我们就看看项目中如何使用的。

  例子的主要基于电子商务中的送货的场景:当用户在电子商务网站上面订购了一个货物的时候,我们就要决定采用哪种方式把货物最终送到用户那里:是航空邮寄,还是轮船配送(轮船又分为很多不同的种类)等等。我们常常根据货物的价格和订购的地址来决定哪种配送方式和速递公司对我们更加的省钱。 

  在这个场景中,OrderService类有一个Dispatch方法,这个方法就来决定用哪种方式把货物送出。 

   请看下图:

  下面我们就通过代码来讲述:(大家可以一起动手)

  1. 创建一个名为ASPPatterns.Chaps.FactoryPattern的解决方案

  2. 添加一个C#类库:ASPPatterns.Chap5.FactoryPattern.Model

  下面就来建立两个业务类:Order实体,和Address值对象(关于实体和值对象,我们之前在DDD中讲过,大家可以参看之前的文章)。

  Order就代表了一个订单,而Address代表的是订单被配送的地址。

  
  
public class Address
{
public string CountryCode { get ; set ; }
}
public class Order
{
public decimal TotalCost { get ; set ; }
public decimal WeightInKG { get ; set ; }
public string CourierTrackingId { get ; set ; }
public Address DispatchAddress { get ; set ; }
}

  因为我们有很多种不同的配送获取的方式,所以我们定义一个配送者的接口,然后让不同的具体类去实现:

  如下:列出IShippingCourier的代码

  
  
public interface IShippingCourier
{
string GenerateConsignmentLabelFor(Address address);
}

  在IShippingCourier中有一个方法,这个方法采用Address为参数,并且返回一个配送速递公司对货物的编码回来。(至于里面怎么送的,我们就不管了,我们认为,只要速递公司把我们的获取的配送编码给我们,我们的获取就肯定已经在他们公司的配送列表中了,送货的工作有他们来做)。

  配送速递的公司有很多,下面我们就选择两个速递公司的实现者:(联邦快递FedEx,敦豪快递DHL) 

  
  
public class DHL : IShippingCourier
{
public string GenerateConsignmentLabelFor(Address address)
{
return " DHL-XXXX-XXXX-XXXX " ;
}
}
public class FedEx : IShippingCourier
{
public string GenerateConsignmentLabelFor(Address address)
{
return " FedExXXXX-XXXX-XXXX " ;
}
}

  下面我们就来创建一个Factory,这个Factory就根据货物和包裹的重量来决定到底最终采用那个配送快递公司,如下:

  
  
public static class UKShippingCourierFactory
{
public static IShippingCourier CreateShippingCourier(Order order)
{
if ((order.TotalCost > 100 ) || (order.WeightInKG > 5 ))
return new DHL();
else
return new RoyalMail();
}
}

  最后,在OrderService的Dispacth(发货)方法就调用Factory创建的IShppingCourier来发送货物。

  上面的代码如果引入IoC,将会更加的灵活!上面的代码对于很多的朋友来说是非常的熟悉了。这里我也不再赘述。

  Decorator

  Decorator属于结构型一种,采用Decorator可以通过组合的方式将新的行为加在现有的对象上,而且不破坏现有类的代码。这种效果的达到是这样做到的:通过继承一个抽象类或者接口,同时也包含同一个将要被装饰的抽象类或者接口的实例。或者说是,这个类同时是IS-A, Has-A.  

  还是先给大家看张图:

  在上面的图中:

  1. 只要实现了IProduct接口的,就说明它是一个产品:DefaultProduct和ProductDecorator

  2. DefaultProduct是一个产品的实现类,而ProductDecorator就是一个来装饰产品的类,或者说通过ProductDecorator,我们为产品引入更多的特性。至于ProductDecorator为产品引入什么特性(例如,使得产品更加的美观,好用,便宜),有不同的具体的ProductDecorator子类来实现。

  下面我们还是来看一个电子商务中的例子:

  场景:在电子购物网站中,我们商品平时是以原价来出售的,如果是节日,那么就会打折,如果商品快断货了,或者很抢手,可能会相应的提价。

  首先我们分析,拿打折来说,可能今天是元旦,打个折;同时可能商家想吐货,也同时打个折,那么这个折扣就是两个打折方案后的累计效果。我们不能总是去改改原有的Product类的逻辑,所以就通过组合的方式,把不同的打折策略组合进去,最后组合成为一个“打折后的商品”来实现我们的变化和需求。 

         我们建立如下的解决方案:       

  首先在Model中添加一个IPrice接口:       

  
  
public interface IPrice
{
decimal Cost { get ; set ; }
}

  为什么要添加这个接口,最直接的原因就是此时商品价格是个变化点,我们这里就把整个变化点抽象出来。

         添加商品类的代码: 

    
    
public class Product
{
public IPrice Price { get ; set ; }
}

  我们添加一个IPrice的实现类BasePrice.这个类就代表了一个商品的默认的价格,就是在没有打折或者提价之前的价格。

  
  
public class BasePrice : IPrice
{
private Decimal _cost;

public decimal Cost
{
get { return _cost; }
set { _cost = value; }
}
}

  其实打折后的价格就是相当于在默认的价格上面做了处理,加了一些点缀,或者说把默认的价格装饰一下就成为了打折后的价格,所以下面再添加一个TradeDiscountPriceDecorator,代表打折之后的价格:

  
  
public class TradeDiscountPriceDecorator : IPrice
{
private IPrice _basePrice;
public TradeDiscountPriceDecorator(IPrice price)
{
_basePrice
= price;
}
public decimal Cost
{
get { return _basePrice.Cost * 0.95m ; }
set { _basePrice.Cost = value; }
}
}

  TradeDiscountPriceDecorator这个类包含一个IPrice接口的引用,同时有继承了这个接口。

  大家可以看到Cost属性,就是把传入的IPrice的Cost属性加了一些修改和调整。如果我们传入BasePrice,经过TradeDiscountPriceDecorator装饰之后,就得到了打折后的价格。然后因为Product引用的是IPrice接口引用,而我们可以把装饰后的结果又是一个IPrice的实现者,那么就在不改变Product情况下搞定了价格变化的问题。

  同理,大家看看下面的代码:可以任意校正价格:     

  
  
public class CurrencyPriceDecorator : IPrice
{
private IPrice _basePrice;
private decimal _exchangeRate;

public CurrencyPriceDecorator(IPrice price, decimal exchangeRate)
{
_basePrice
= price;
_exchangeRate
= exchangeRate;
}

public decimal Cost
{
get { return _basePrice.Cost * _exchangeRate; }
set { _basePrice.Cost = value; }
}
}

  主要的代码写完了,下面我们添加一些扩张方法来辅助: 

  
  
public static class ProductCollectionExtensionMethods
{
public static void ApplyCurrencyMultiplier( this IEnumerable < Product > products)
{
foreach (Product p in products)
p.Price
= new CurrencyPriceDecorator(p.Price, 0.78m );
}

public static void ApplyTradeDiscount( this IEnumerable < Product > products)
{
foreach (Product p in products)
p.Price
= new TradeDiscountPriceDecorator(p.Price);
}
}

  代码很简单,就是把商品的价格装饰下。

  为了整个例子的完整,我们写出一些数据访问代码:

  
  
public interface IProductRepository
{
IEnumerable
< Product > FindAll();
}
public class ProductService
{
private IProductRepository _productRepository;

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

public IEnumerable < Product > GetAllProducts()
{
IEnumerable
< Product > products = _productRepository.FindAll();

products.ApplyTradeDiscount();

products.ApplyCurrencyMultiplier();

return products;
}
}

  以上就是本篇的内容,讲述的很粗略,希望见谅,还没有写完,待续!

目录
相关文章
|
5天前
|
SQL 开发框架 .NET
ASP连接SQL数据库:从基础到实践
随着互联网技术的快速发展,数据库与应用程序之间的连接成为了软件开发中的一项关键技术。ASP(ActiveServerPages)是一种在服务器端执行的脚本环境,它能够生成动态的网页内容。而SQL数据库则是一种关系型数据库管理系统,广泛应用于各类网站和应用程序的数据存储和管理。本文将详细介绍如何使用A
21 3
|
2月前
|
设计模式 存储 前端开发
揭秘.NET架构设计模式:如何构建坚不可摧的系统?掌握这些,让你的项目无懈可击!
【8月更文挑战第28天】在软件开发中,设计模式是解决常见问题的经典方案,助力构建可维护、可扩展的系统。本文探讨了.NET中三种关键架构设计模式:MVC、依赖注入与仓储模式,并提供了示例代码。MVC通过模型、视图和控制器分离关注点;依赖注入则通过外部管理组件依赖提升复用性和可测性;仓储模式则统一数据访问接口,分离数据逻辑与业务逻辑。掌握这些模式有助于开发者优化系统架构,提升软件质量。
46 5
|
2月前
|
XML 开发框架 .NET
.NET框架:软件开发领域的瑞士军刀,如何让初学者变身代码艺术家——从基础架构到独特优势,一篇不可错过的深度解读。
【8月更文挑战第28天】.NET框架是由微软推出的统一开发平台,支持多种编程语言,简化应用程序的开发与部署。其核心组件包括公共语言运行库(CLR)和类库(FCL)。CLR负责内存管理、线程管理和异常处理等任务,确保代码稳定运行;FCL则提供了丰富的类和接口,涵盖网络、数据访问、安全性等多个领域,提高开发效率。此外,.NET框架还支持跨语言互操作,允许开发者使用C#、VB.NET等语言编写代码并无缝集成。这一框架凭借其强大的功能和广泛的社区支持,已成为软件开发领域的重要工具,适合初学者深入学习以奠定职业生涯基础。
94 1
|
2月前
|
存储 监控 安全
大数据架构设计原则:构建高效、可扩展与安全的数据生态系统
【8月更文挑战第23天】大数据架构设计是一个复杂而系统的工程,需要综合考虑业务需求、技术选型、安全合规等多个方面。遵循上述设计原则,可以帮助企业构建出既高效又安全的大数据生态系统,为业务创新和决策支持提供强有力的支撑。随着技术的不断发展和业务需求的不断变化,持续优化和调整大数据架构也将成为一项持续的工作。
|
3月前
|
NoSQL Redis UED
业务架构问题之在流程建模中,“定职责”的重要性是什么,流程建模中的交互设计原则是什么
业务架构问题之在流程建模中,“定职责”的重要性是什么,流程建模中的交互设计原则是什么
|
2月前
|
消息中间件 监控 Java
解锁Spring Cloud微服务架构的奥秘:深度剖析拆分原则,打造高内聚低耦合的业务创新引擎!
【8月更文挑战第3天】踏入微服务领域,Spring Cloud以丰富组件助力高效系统构建。微服务拆分需遵循原则确保系统高内聚低耦合且能适应变化。首要原则为单一职责,每个服务专注一个业务功能,降低复杂度并提高可维护性。其次,追求高内聚低耦合以减少服务间影响。围绕业务域拆分有助于保持逻辑清晰及团队协作。处理数据一致性问题时,考虑采用最终一致性模型。Spring Cloud提供Eureka、Zuul/Gateway、Sleuth和Config等工具支持服务发现、路由、跟踪及配置管理,共同构建灵活健壮的微服务架构。
64 2
|
3月前
|
存储 设计模式 前端开发
软件架构设计的原则与模式:构建高质量系统的基石
【7月更文挑战第26天】软件架构设计是构建高质量软件系统的关键。遵循高内聚、低耦合、单一职责等设计原则,并灵活运用分层架构、微服务架构、客户端-服务器架构等设计模式,可以帮助我们设计出更加灵活、可扩展、可维护的软件系统。作为开发者,我们应该不断学习和实践这些原则与模式,以提升自己的架构设计能力,为团队和用户提供更加优秀的软件产品。
|
2月前
|
边缘计算 Kubernetes 持续交付
构建高效后端系统:面向未来的架构设计原则
【8月更文挑战第8天】在技术飞速发展的今天,后端系统的架构设计显得尤为关键。本文将探讨如何通过采用微服务、容器化及自动化等现代技术手段,来构建一个可扩展、高可用且易于维护的后端系统。我们将深入分析这些技术背后的原理及其在实际场景中的应用,同时也会讨论如何在保障数据一致性和系统安全性的前提下,提升系统的响应速度和处理能力。
|
3月前
|
搜索推荐
业务系统架构实践问题之有效地实现“域间不可见”原则问题如何解决
业务系统架构实践问题之有效地实现“域间不可见”原则问题如何解决
|
3月前
|
监控 Java API
Java面试题:解释微服务架构的概念及其优缺点,讨论微服务拆分的原则。
Java面试题:解释微服务架构的概念及其优缺点,讨论微服务拆分的原则。
67 0