應用程式分層設計 應用程式分層屬於關註點分離的一種形式,可以通過命名空間、文件夾或採用單獨的項目來實現。 下圖為一個採用分層設計的項目結構 ASPPatterns.Chap3.Layered.Repository依賴於ASPPatterns.Chap3.Layered.Model ASPPatter ...
應用程式分層設計
應用程式分層屬於關註點分離的一種形式,可以通過命名空間、文件夾或採用單獨的項目來實現。
下圖為一個採用分層設計的項目結構
- ASPPatterns.Chap3.Layered.Repository依賴於ASPPatterns.Chap3.Layered.Model
- ASPPatterns.Chap3.Layered.Service依賴於ASPPatterns.Chap3.Layered.Repository和ASPPatterns.Chap3.Layered.Model
- ASPPatterns.Chap3.Layered.Presentation依賴於ASPPatterns.Chap3.Layered.Service和ASPPatterns.Chap3.Layered.Model
- ASPPatterns.Chap3.Layered.ConsoleApp依賴於ASPPatterns.Chap3.Layered.Repository、ASPPatterns.Chap3.Layered.Model、ASPPatterns.Chap3.Layered.Service和ASPPatterns.Chap3.Layered.Presentation
業務層
通過創建一個領域模型來存放所有正在建模的對象的業務有關的行為和數據。
//示例:商品折扣業務處理
namespace ASPPatterns.Chap3.Layered.Model
{
/// <summary>
/// 折扣介面
/// 通過策略模式,可以在運行時選擇和改變演算法(折扣)
/// </summary>
public interface IDiscountStrategy
{
decimal ApplyExtraDiscountsTo(decimal OriginalSalePrice);
}
/// <summary>
/// 商品折扣策略-打88折
/// </summary>
public class TradeDiscountStrategy : IDiscountStrategy
{
public decimal ApplyExtraDiscountsTo(decimal OriginalSalePrice)
{
return OriginalSalePrice * 0.88M;
}
}
/// <summary>
/// 無折扣
/// </summary>
public class NullDiscountStrategy : IDiscountStrategy
{
public decimal ApplyExtraDiscountsTo(decimal OriginalSalePrice)
{
return OriginalSalePrice;
}
}
/// <summary>
/// 將折扣策略應用到商品的價格
/// </summary>
public class Price
{
private IDiscountStrategy _discountStrategy = new NullDiscountStrategy();
public decimal _rrp;
private decimal _sellingPrice;
public Price(decimal rrp, decimal sellingPrice)
{
_rrp = rrp;
_sellingPrice = sellingPrice;
}
public decimal RRP { get { return _rrp; } }
public decimal SellingPrice
{
get { return _discountStrategy.ApplyExtraDiscountsTo(_sellingPrice); }
}
public decimal Discount
{
get
{
if (RRP > SellingPrice)
return RRP - SellingPrice;
else
return 0;
}
}
public decimal Savings
{
get
{
if (RRP > SellingPrice)
return 1 - (SellingPrice / RRP);
else
return 0;
}
}
/// <summary>
/// 通過Set方法註入折扣策略
/// </summary>
public void SetDiscountStrategyTo(IDiscountStrategy discountStrategy)
{
_discountStrategy = discountStrategy;
}
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public Price Price { get; set; }
}
public enum CustomerType
{
Standard = 0,
Trade
}
/// <summary>
/// 折扣策略的選擇
/// </summary>
public static class DiscountFactory
{
public static IDiscountStrategy GetDiscountStrategy(CustomerType customerType)
{
switch (customerType)
{
case CustomerType.Trade:
return new TradeDiscountStrategy();
default:
return new NullDiscountStrategy();
}
}
}
/// <summary>
/// 服務層與數據存儲交互,意見所商品。使用倉儲模式來實現該功能,但只是指定資源庫介面,
/// 這裡因為不希望model層牽涉到諸如使用什麼類型的數據存儲或使用什麼類型的技術來查詢等細節
/// </summary>
public interface IProductRepository
{
IList<Product> FindAll();
}
/// <summary>
/// 服務類需要能夠將給定的折扣策略應用到一組商品。
/// 通過擴展方法可以更靈活的創建一個自定義集合來實現該功能
/// </summary>
public static class ProductListExtensionMenthods
{
public static void Apply(this IList<Product> products, IDiscountStrategy discountStrategy)
{
foreach (var item in products)
{
item.Price.SetDiscountStrategyTo(discountStrategy);
}
}
}
/// <summary>
/// 客戶端與領域交互的服務類
/// </summary>
public class ProductService
{
private IProductRepository _productRepository;
public ProductService(IProductRepository productRepository)
{
_productRepository = productRepository;
}
public IList<Product> GetAllProductsFor(CustomerType customerType)
{
IDiscountStrategy discountStrategy = DiscountFactory.GetDiscountStrategy(customerType);
IList<Product> products = _productRepository.FindAll();
products.Apply(discountStrategy);
return products;
}
}
}
服務層
服務層的作用就是充當應用的入口,為表示層提供了強類型視圖模型。所謂視圖模型就是平常界面上綁定的實體類。
namespace ASPPatterns.Chap3.Layered.Service
{
public class ProductViewModel
{
public int ProductId { get; set; }
public string Name { get; set; }
public string RRP { get; set; }
public string SellingPrice { get; set; }
public string Discount { get; set; }
public string Savings { get; set; }
}
/// <summary>
/// 為了讓客戶端與服務層交互,使用Reuqest/Response消息模式
/// Reuqest由客戶端提供,它將攜帶必要的參數,如本示例中的CustomerType
/// </summary>
public class ProductListRequest
{
public CustomerType CustomerType { get; set; }
}
/// <summary>
/// 響應客戶端請求的內容
/// </summary>
public class ProductListResponse
{
public bool Success { get; set; }
public string Message { get; set; }
public IList<ProductViewModel> Products { get; set; }
}
/// <summary>
/// 將Product轉換成ProductViewModel
/// </summary>
public static class ProductMapperExtensionMethods
{
public static IList<ProductViewModel> ConvertToProductListViewModel(this IList<Product> products)
{
IList<ProductViewModel> productViewModels = new List<ProductViewModel>();
foreach (var item in products)
{
productViewModels.Add(item.ConvertToProductViewModel());
}
return productViewModels;
}
public static ProductViewModel ConvertToProductViewModel(this Product product)
{
ProductViewModel productViewModel = new ProductViewModel();
productViewModel.ProductId = product.Id;
//....其他賦值
return productViewModel;
}
}
/// <summary>
/// ProductService與領域模型服務交互,檢索商品列表,並將其轉換成ProductListViewModel列表
/// </summary>
public class ProductService
{
private Model.ProductService _productService;
public ProductService(Model.ProductService productService)
{
_productService = productService;
}
public ProductListResponse GetAllProductsFor(ProductListRequest productListRequest)
{
ProductListResponse productListResponse = new ProductListResponse();
try
{
IList<Product> productEntities = _productService.GetAllProductsFor(productListRequest.CustomerType);
productListResponse.Products = productEntities.ConvertToProductListViewModel();
productListResponse.Success = true;
}
catch (Exception)
{
productListResponse.Success = false;
}
return productListResponse;
}
}
}
數據訪問層
主要是操作資料庫數據的讀寫。比較常用的框架有Entity Framework、Hibernate,有時候像Dapper這種輕量級的類庫更靈活。
namespace ASPPatterns.Chap3.Layered.Repository
{
public class ProductRepository : IProductRepository
{
public IList<Model.Product> FindAll()
{
//linq to sql
return new List<Model.Product>();
}
}
}
表示層
為了將表示邏輯與用戶界面分離,一般使用MVP(視圖-模型-展示)模式。擁有表示層的好處是可以很容易的測試數據的表示以及系統之間的交互。
namespace ASPPatterns.Chap3.Layered.Presentation
{
public interface IProductListView
{
void Display(IList<ProductViewModel> products);
Model.CustomerType CustomerType { get; }
string ErrorMessage { set; }
}
/// <summary>
/// 呈現器負責獲取數據、處理用戶事件並通過視圖的介面更新視圖
/// </summary>
public class ProductListPresenter
{
private IProductListView _productListView;
private Service.ProductService _productService;
public ProductListPresenter(IProductListView productListView, Service.ProductService productService)
{
_productListView = productListView;
_productService = productService;
}
public void Display()
{
ProductListRequest productListRequest = new ProductListRequest();
productListRequest.CustomerType = _productListView.CustomerType;
ProductListResponse productListResponse = _productService.GetAllProductsFor(productListRequest);
if (productListResponse.Success)
{
_productListView.Display(productListResponse.Products);
}
else
{
_productListView.ErrorMessage = productListResponse.Message;
}
}
}
}
UI(用戶體驗)層
主要是實現數據的呈現以及與用戶的交互。它不需要關註數據是如何從資料庫獲取的,只要負責處理用戶事件並轉發調用,將工作委托給表示層的Presenter。
總結
從上面的例子中我們可以看出程式分層設計的好處,應用程式的關註點分解到不同的層次,可以使程式更易於理解和維護。
《ASP.NET 設計模式》