走向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 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的代碼
{
string GenerateConsignmentLabelFor(Address address);
}
在IShippingCourier中有一個方法,這個方法采用Address為參數,并且返回一個配送速遞公司對貨物的編碼回來。(至于里面怎么送的,我們就不管了,我們認為,只要速遞公司把我們的獲取的配送編碼給我們,我們的獲取就肯定已經在他們公司的配送列表中了,送貨的工作有他們來做)。
配送速遞的公司有很多,下面我們就選擇兩個速遞公司的實現者:(聯邦快遞FedEx,敦豪快遞DHL)
{
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 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接口:
{
decimal Cost { get; set; }
}
為什么要添加這個接口,最直接的原因就是此時商品價格是個變化點,我們這里就把整個變化點抽象出來。
添加商品類的代碼:
{
public IPrice Price { get; set; }
}
我們添加一個IPrice的實現類BasePrice.這個類就代表了一個商品的默認的價格,就是在沒有打折或者提價之前的價格。
{
private Decimal _cost;
public decimal Cost
{
get { return _cost; }
set { _cost = value; }
}
}
其實打折后的價格就是相當于在默認的價格上面做了處理,加了一些點綴,或者說把默認的價格裝飾一下就成為了打折后的價格,所以下面再添加一個TradeDiscountPriceDecorator,代表打折之后的價格:
{
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情況下搞定了價格變化的問題。
同理,大家看看下面的代碼:可以任意校正價格:
{
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 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);
}
}
代碼很簡單,就是把商品的價格裝飾下。
為了整個例子的完整,我們寫出一些數據訪問代碼:
{
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;
}
}
以上就是本篇的內容,講述的很粗略,希望見諒,還沒有寫完,待續!