此外,领域对象对数据访问层数据对象的封装,也有利于表示层对业务逻辑层的调用。在三层式架构中,表示层应该是对于数据访问层是“无知”的,这样既减少了层与层间的依赖关系,也能有效避免“循环依赖”的后果。
值得商榷的是Cart类的Total属性。其值的获取是通过遍历购物车集合,然后累加价格与商品数量的乘积。这里显然简化了业务逻辑,而没有充分考虑需求的扩展。事实上,这种获取购物车总价格的算法,在大多数情况下仅仅是其中的一种策略而已,我们还应该考虑折扣的情况。例如,当总价格超过100元时,可以给与顾客一定的折扣,这是与网站的促销计划相关的。除了给与折扣的促销计划外,网站也可以考虑赠送礼品的促销策略,因此我们有必要引入Strategy模式,定义接口IOnSaleStrategy:
public interface IOnSaleStrategy { decimal CalculateTotalPrice(Dictionary cartItems); }
如此一来,我们可以为Cart类定义一个有参数的构造函数:
private IOnSaleStrategy m_onSale; public Cart(IOnSaleStrategy onSale) { m_onSale = onSale; }
那么Total属性就可以修改为:
public decimal Total { get {return m_onSale.CalculateTotalPrice(cartItems);} }
如此一来,就可以使得Cart类能够有效地支持网站推出的促销计划,也符合开-闭原则。同样的,这种设计方式也是Domain Model模式的体现。修改后的设计如图5-1所示:
图5-1 引入Strategy模式
作为一个B2C的电子商务架构,它所涉及的业务领域已为大部分设计师与开发人员所熟悉,因而在本例中,与领域专家的合作显得并不那么重要。然而,如果我们要开发一个成功的电子商务网站,与领域专家的合作仍然是必不可少的。以订单的管理而言,如果考虑复杂的商业应用,就需要管理订单的跟踪(Tracking),与网上银行的合作,账户安全性,库存管理,物流管理,以及客户关系管理(CRM)。整个业务过程却涵盖了诸如电子商务、银行、物流、客户关系学等诸多领域,如果没有领域专家的参与,业务逻辑层的设计也许会“败走麦城”。
5.4 与数据访问层的通信
业务逻辑层需要与数据访问层通信,利用数据访问层访问数据库,因此业务逻辑层与数据访问层之间就存在依赖关系。在数据访问层引入接口程序集以及数据工厂的设计前提下,能够做到两者间关系为弱依赖。我们从业务逻辑层的引用程序集中可以看到,BLL模块并没有引用SQLServerDAL和OracleDAL程序集。在业务逻辑层中,有关数据访问层中数据对象的调用,均利用多态原理定义了抽象的接口类型对象,然后利用工厂对象的工厂方法创建具体的数据对象。如PetShop.BLL.PetShop领域对象所示:
namespace PetShop.BLL { public class Product { //根据工厂对象创建IProduct接口类型实例; private static readonly IProduct dal = PetShop.DALFactory.DataAccess.CreateProduct(); //调用IProduct对象的接口方法GetProductByCategory(); public IList GetProductsByCategory(string category) { // 如果为空则新建List对象; if(string.IsNullOrEmpty(category)) return new List (); // 通过数据访问层的数据对象访问数据库; return dal.GetProductsByCategory(category); } //其他方法略; } }
在领域对象Product类中,利用数据访问层的工厂类DALFactory.DataAccess创建PetShop.IDAL.IProduct类型的实例,如此就可以解除对具体程序集SQLServerDAL或OracleDAL的依赖。只要PetShop.IDAL的接口方法不变,即使修改了IDAL接口模块的具体实现,都不会影响业务逻辑层的实现。这种松散的弱耦合关系,才能够最大程度地支持架构的可扩展。
领域对象Product实际上还完成了对数据对象Product的封装,它们暴露在外的接口方法是一致地,正是通过封装,使得表示层可以完全脱离数据库以及数据访问层,表示层的调用者仅需要关注业务逻辑层的实现逻辑,以及领域对象暴露的接口和调用方式。事实上,只要设计合理,规范了各个层次的接口方法,三层式架构的设计完全可以分离开由不同的开发人员同时开发,这就可以有效地利用开发资源,缩短项目开发周期。
5.5 面向接口设计
也许是业务逻辑比较简单地缘故,在业务逻辑层的设计中,并没有秉承在数据访问层中面向接口设计的思想。除了完成对插入订单策略的抽象外,整个业务逻辑层仅以BLL模块实现,没有为领域对象定义抽象的接口。因而PetShop的表示层与业务逻辑层就存在强依赖关系,如果业务逻辑层中的需求发生变更,就必然会影响表示层的实现。唯一可堪欣慰的是,由于我们采用分层式架构将用户界面与业务领域逻辑完全分离,一旦用户界面发生更改,例如将B/S架构修改为C/S架构,那么业务逻辑层的实现模块是可以完全重用的。