设计模式系列:仓储模式

仓储模式的实现

仓储Repository模式已经成为最主流的模式,数据库持久化很长时间以来是一个讨论热点,目前主要问题是:主流软件并不容易有效地将需要存储的数据映射到外部存储空间如关系数据库或NoSQL数据库。

技术难点虽然已经被一些ORM工具如Hibernate等解决了,过去,我们通常使用DAOORM来进行业务对象和持久化数据表之间进行转换,这些技术很好,但是他们还是属于底层技术,并不能透明地和我们统一语言整合。下面的问题是如何将持久化动作和对象获取方式以及领域模型Domain Model结合起来,更进一步地说:如何更加统一我们的语言(Ubiquitous Language)(不至于出现Hibernate等底层技术语言,统一为业务语言)

一个整合持久化技术的好办法是仓储Repositories,在Evans书籍中定义它是:一种机制,用于封装存储获取搜寻一群对象集合等行为。这个定义很容易向领域专家解释,整合到统一语言中。

第一:命名
一般人仓储类如下命名:


  class OrderRepository {
    List<Order>  getOrdersFor(Account a){...}
  }


Rodrigo Yoshima 认为应该如下取名:


  class AllOrders {
     List<Order>  belongingTo(Account a){...}
  }


似乎是一个小的改变,但是有帮助得多,如果两个仓储包含不属于他们的方法,我们如何分辨他们呢?


  //classic naming style
  class UserRepository{
   
User retrieveUserByLogin(String  login){...}
   void submitOrder(Order  order){...}
  }


客户端调用代码:


  User u = userRepository.retrieveUserByLogin(“pcalcado”);
  userRepository.submitOrder(new Order());



Yoshima的取名方式如下:


  //Yoshima’s naming style
  class AllUsers{
   
User withLogin(String login){...}
   void submitOrder(Order  order){...}
  }
   
  //client code //非常符合英语风格,象说话一样。
  User u = allusers.withLogin(“pcalcado”);
  allusers.submitOrder(new Order());



一定要记住:你使用的语言方式会影响你如何思考。以retrieveget为开始方法名是坏味道。

第二:避免方法爆炸
一个好的仓储将领域概念建模在其接口中,让我们看一个业务规则:
每个订单在周末都要增加10%的附加费。

如果我们要显示所有订单,如下:


  List<Order> surchargedOrders = allOrders.placed(user, IN_A_SATURDAY);
  surchargedOrders.addAll(allOrders.placed(user, IN_A_SUNDAY));
  return surchargedOrders;


这虽然工作很好,但是忘记了抽象,增加附加费这个规则是不应该暴露给调用客户端的,如下会更好些:
return allOrders.surchargedFor(user);

为了实现这点,可能需要多个实体反复查询,你可以使用规格模式Specification来实现,如下:

Specification surcharged = specifications.surcharged();
return allOrders.thatAre(user, surchanged);

如果我们要获取没有附加费或者有附加费的订单,需要两个仓储AllOrders SurchargedOrders.如下:


  //returns all orders
  return allOrders.from(user);
   

  //returns only orders with applied  surcharge
  return surchargedOrders.from(user);



Subset Repositories
子集仓储一般被建模成带有参数实例的仓储类,如下:


  //a base Repository
  class Users {
   
private User.Status  desiredStatus = null;
   
   public Users(){
     this(User.Status.ANY);
   }
   
   public Users(User.Status  desiredStatus){
     this.desiredStatus=  desiredStatus;
   }
   //methods  go here...
  }
   

  //instantiated somewhere as
  private Users allUsers = new Users();
  private Users activeUsers = new Users(User.Status.ACTIVE);
  private Users inactiveUsers = new Users(User.Status.INACTIVE);



第三:只有一个类型
如下代码,虽然使用了好的命名规则


  public interface AllServices {
   
      List<Service>  belongingTo(List<Account> accounts);
   
      Service withNumber(String  serviceNumber);
   
      List<Service>  relatedTo(Service otherService);
   
      List<Product>  allActiveProductsBelongingTo(List<Account> accounts);
   
      List<Product>  allProductsBelongingTo(List<Account> accounts);
   
      ContractDetails  retrieveContractDetails(String serviceNumber);
  }



方法返回了Service类型集合,还返回了Product集合,是多个集合,好的设计最好返回同一个类型集合:


  public interface AllServices {
   
      List<Service>  belongingTo(List<Account> accounts);
   
      Service withNumber(String serviceNumber);
   
      List<Service>  relatedTo(Service otherService);
  }
   
  public interface AllProducts {
   
      List<Product>  activeBelongingTo(List<Account> accounts);
   
      List<Product>  belongingTo(List<Account> accounts);
  }
   
  public interface AllContractDetails {
     ContractDetails  forServiceNumber(String serviceNumber);
  }


如果特别情形需要,你可以创建一个Wrapper类,将这些方法都放入其中,如下:


  public class BillingSystemGateway implements AllServices,  AllProducts , AllContractDetails {
   
      List<Service>  belongingTo(List<Account> accounts){...}
   
      Service withNumber(String  serviceNumber) {...}
   
      List<Service>  relatedTo(Service otherService) {...}
   
  List<Product> activeBelongingTo(List<Account> accounts) {...}
   
  List<Product> belongingTo(List<Account> accounts) {...}
   
  ContractDetails forServiceNumber(String serviceNumber) {...}


最后:仓储不只是持久,仓储经常用于对象存储持久化,但是这不一定是这种情况,也适合于系统整合,甚至简单内存缓存中集合返回值对象等等。

一定要记住:仓储好处是高调说明对象来自某个地方,将这些对象作为统一语言的一部分,仓储尽可能贴近我们的领域模型。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值