走向.NET架构设计—第五章—业务层模式,原则,实践(中篇)
走向.NET架构设计—第五章—业务层模式,原则,实践(中篇)
前言:设计模式并不是什么很高深的东西,至少不是那么“神乎其神”。说到底,设计模式就是一些设计思想。下面我们就走进项目,看看这些项目中这些思想是如何体现的。本系列文章会在后续文章中陆陆续续的,在恰当的时候介绍一些相应的设计模式,而不是一股脑的一起上。
本篇的议题如下:
架构模式
设计模式
设计原则
设计模式
本篇文章主要是讨论的在业务层可以采用的或者常用的一些设计模式:
Factory Method
Decorator
Template Method
State
Strategy
Factory Method
相信很多朋友对这个模式很熟悉了,平时在项目中或多或少总能看到Factory, Provider等。确实Factory Method一种创建型的模式,它的主要目的就是隐藏对象创建的细节。也就是说,客户程序(或者成为调用者)不用特定来什么创建某一种具体的类,也不依赖于特定的类,而且依赖接口或者抽象类,这样就达到了解耦,专业点的说法就是“依赖倒置”,更加直白的说法就是:客户程序可以使用很多不同的实现类,而保持代码不变。因为在需要的时候,传入一些信息,Factory Methods就返回接口或者抽象类的实现类。
很多情况下,我们一般是这样来使用Factory Method模式的:建立一个Factory类,这个类有一个静态的方法,这个方法返回一个抽象的类或者接口。然后,客户程序(或者调用程序)就传入一些信息给Factory类来,要求Factory来创建相对应,需要的具体的实现类。
下面我们就看看一个Factory Method的UML图:
在图中,可以得出以下几点信息:
q Client类通过Factory类来获取实现了IProduct的具体子类。
q Client类依赖IProduct接口,而不是依赖具体的子类。
q Factory类复杂创建具体的子类,向Client隐藏具体的细节。
现在网上购物已经很流行了,在选择支付方式的时候,一般有几种选择,比如:网银支付,货到付款支付等。对于网银支付,又可以进一步细分为:中国银行支付、工商银行支付等。不同的银行支付,最后调用的接口都不一样,而且以后系统可能会支持更多的银行。所以在设计支付功能的时候需要考虑到扩展性。本例将会介绍如何采用工厂方法模式来实现支付功能。
请看下图:
下面我们就通过代码来讲述:(大家可以一起动手)
IPayment支付方式的接口代码如下所示:
{
bool Payfor(decimal money);
}
支付实现者ABCPayment支付类的代码如下所示:
{
public bool Payfor(decimal money)
{
//调用中国农业银行提供的支付接口进行支付
return true;
}
}
ICBCPayment、AOCPayment的实现同理。
PaymentFactory的任务就是根据传入的条件来创建不同的具体支付者:
{
public static IPayment CreatePayment(string bank)
{
IPayment result = null;
switch (bank)
{
case "ABC":
result = new ABCPayment();
break;
case "ICBC":
result = new ICBCPayment();
break;
}
return result;
}
}
当然,可以采用更好的方式来实现PaymentFactory的CreatePayment方法,例如采用配置文件,动态加载程序集的方式。
Decorator
为了更好地理解“装饰”的概念,我们首先抛开枯燥的阐述,来看看现在流行的网游。在网游中玩家可以给自己的账号购买不同的装备,而且不同的装备其特性也不一样,比如重量、防御能力、攻击能力等。下面就以装备中的铠甲为引子来讲述“装饰”。
下面,我们就看看如何实现铠甲的升级功能:从最初的青铜甲一步步升级到麒麟甲。
其中:
Knight:代表武将。
IArmour:铠甲接口。QTArmour为青铜铠甲,BLGJArmour为百炼钢甲,QLArmour为麒麟甲。
每次铠甲升级,都是通过工厂武将创建新的铠甲子类替换原来的铠甲。
如果以后有新的铠甲或要对现有的铠甲功能进行增强,可以采用继承的方式实现。例如现在麒麟铠甲要增加隐形功能,本着“开放关闭原则”,最容易想到的就是添加一个新的子类,从QLArmour(青铜铠甲)继承现有的功能,同时再加上新的功能。如果再要新加上其他功能,其步骤也与此类似,原本就想通过继承来重用现有的代码,现在这个目的达到了,但是最后系统中存在了很多的功能相近的类,导致类的数目急剧膨胀。
如果能够在现有铠甲上面通过不断的改造,增加新的功能,类似于变形金刚变身那样,使加入了新功能的青铜甲铠甲演变成麒麟甲,成为新类型的铠甲,那么类的数目就不会像之前那样膨胀了。
网游的例子到此暂告一个段落,主要就是想让大家了解一下“装饰”的含义,至于如何实现,在讲完下面的例子之后大家就清楚了。
要遵循“开放关闭”原则,同时又要规避使用继承带来的类膨胀问题,如何实现?装饰者模式回答了这个问题。
如图,先不管图中类名的定义,首先看看主要类的含义:
(1) OnlineSitePriceDecorator代表购物站点的打折。
(2) SupplierPriceDecorator代表供应商的打折。
(3) PolicyPriceDecorator代表政府要求的打折。
在打折的时候,需要考虑:打折就是改变产品的价格,而很多时候,价格往往只是产品的一个属性,例如,通常会采用decimal等数据类型。
现在因为价格总是在不断地根据打折算法的变换而变化,那么现在“价格”就成了一个变化点;另一方面,更加准确的说:打折算法是用在价格之上的,这样才能实现对产品的打折。考虑把价格这个变化点引出去,成为一个业务实体Price,然后把打折算法都累积在这个实体上面进行,产品类Product丝毫不知道Price类是否应用了打折算法,只管引用Price获取最后的价格。
{
decimal Cost { get; set; }
}
public class Product
{
public string Name { get; set; }
public IPrice Price { get; set; }
}
下面,开始添加不同打折算法的实现。
首先我们来回想一下之前装备升级的例子,我们是希望通过不断地改造青铜甲,在原有的基础上加上新的功能,最后达到装备升级的目的。此时,在价格打折上面,因为同时要采用很多的打折算法,也类似于给价格不断的“升级”:在原有的价格上不断进行包装。所以OnlineSitePriceDecorator购物网站推出的打折方法如下:
{
private IPrice price;
public OnlineSitePriceDecorator(IPrice price)
{
this.price = price;
}
public decimal Cost
{
get
{
return price.Cost * 0.9M;
}
set
{
price.Cost = value;
}
}
}
从上面的代码中我们可以看出,拿到商品的原价后进行包装:proce.Cost*0.9M。在经过OnlineSitePriceDecorator包装之后把价格抛出来,进行下一个包装:供应商打折。供应商打折的代码如下所示:
{
private IPrice price;
public SupplierPriceDecorator(IPrice price)
{
this.price = price;
}
public decimal Cost
{
get
{
return price.Cost * 0.85M;
}
set
{
price.Cost = value;
}
}
}
下面看看如何实现累积打折算法,代码如下:
{
private IProductRepository productRepository;
public ProductService(IProductRepository productRepository)
{
this.productRepository = productRepository;
}
public List<Product> GetProducts()
{
var products = productRepository.GetAllProduct();
products.ApplyDiscount();
return products;
}
}
//其中ApplyDiscount为List的扩展方法,如下所示:
public static class DiscountExtension
{
public static List<Product> ApplyDiscount(this List<Product> products)
{
if (products != null && products.Count > 0)
{
foreach (var product in products)
{
product.Price = new OnlineSitePriceDecorator(product.Price);
product.Price = new SupplierPriceDecorator(product.Price);
product.Price = new PolicyPriceDecorator(product.Price, 0.88M);
}
}
return products;
}
价格包装的过程如图所示。
以上就是本篇的内容,讲述的很粗略,希望见谅,还没有写完,待续!J