一起谈.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月前
|
存储 缓存 安全
某鱼电商接口架构深度剖析:从稳定性到高性能的技术密码
某鱼电商接口架构揭秘:分层解耦、安全加固、性能优化三维设计,实现200ms内响应、故障率低于0.1%。详解三层架构、多引擎存储、异步发布、WebSocket通信与全链路防护,助力开发者突破电商接口“三难”困境。
|
5月前
|
Java Linux 虚拟化
【Docker】(1)Docker的概述与架构,手把手带你安装Docker,云原生路上不可缺少的一门技术!
1. Docker简介 1.1 Docker是什么 为什么docker会出现? 假定您在开发一款平台项目,您的开发环境具有特定的配置。其他开发人员身处的环境配置也各有不同。 您正在开发的应用依赖于您当前的配置且还要依赖于某些配置文件。 您的企业还拥有标准化的测试和生产环境,且具有自身的配置和一系列支持文件。 **要求:**希望尽可能多在本地模拟这些环境而不产生重新创建服务器环境的开销 问题: 要如何确保应用能够在这些环境中运行和通过质量检测? 在部署过程中不出现令人头疼的版本、配置问题 无需重新编写代码和进行故障修复
510 3
|
5月前
|
存储 人工智能 搜索推荐
拔俗AI助教系统:基于大模型与智能体架构的新一代教育技术引擎
AI助教融合大语言模型、教育知识图谱、多模态感知与智能体技术,重构“教、学、评、辅”全链路。通过微调LLM、精准诊断错因、多模态交互与自主任务规划,实现个性化教学。轻量化部署与隐私保护设计保障落地安全,未来将向情感感知与教育深度协同演进。(238字)
579 0
|
开发框架 前端开发 .NET
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
563 0
|
开发框架 前端开发 JavaScript
ASP.NET MVC 教程
ASP.NET 是一个使用 HTML、CSS、JavaScript 和服务器脚本创建网页和网站的开发框架。
326 7
|
存储 开发框架 前端开发
[回馈]ASP.NET Core MVC开发实战之商城系统(五)
经过一段时间的准备,新的一期【ASP.NET Core MVC开发实战之商城系统】已经开始,在之前的文章中,讲解了商城系统的整体功能设计,页面布局设计,环境搭建,系统配置,及首页【商品类型,banner条,友情链接,降价促销,新品爆款】,商品列表页面,商品详情等功能的开发,今天继续讲解购物车功能开发,仅供学习分享使用,如有不足之处,还请指正。
408 0
|
开发框架 前端开发 .NET
[回馈]ASP.NET Core MVC开发实战之商城系统(一)
[回馈]ASP.NET Core MVC开发实战之商城系统(一)
536 0
|
SQL 开发框架 前端开发
[回馈]ASP.NET Core MVC开发实战之商城系统(开篇)
[回馈]ASP.NET Core MVC开发实战之商城系统(开篇)
500 0
|
存储 开发框架 前端开发
ASP.NET MVC 迅速集成 SignalR
ASP.NET MVC 迅速集成 SignalR
334 0

热门文章

最新文章