代码改变世界

【讨论二】服务层(Service)的功能参数列表的粒度

2011-06-12 13:21  bugfly  阅读(1678)  评论(11编辑  收藏  举报
上篇【讨论二】服务层(Service)的功能参数列表的粒度 可能缺少代码段和图片,大家都不怎么来参与,所以在原有的基础上加入这些元素,希望大家能给点灵感我。


我们一般在设计服务层的时候总会有这种情况,究竟参数列表是简单类型还是对象类型?

首先我们从比较常见的场景入手,修改订单。这里我们先不考虑它是否应该归属于领域对象。

根据场景很自然就能提炼出一个功能:EditOrder(XXX),我们开始看看参数列表的写法的种类。我大概想到3种。

1.void EditOrder(string orderName,string orderText,float Money);

2.void EditOrder(Order order);

3.void EditOrder(OrderDTO dto);

先看看我对这三种设计的见解。

第一种:很直观,也是最早被我们使用的方式,但这种设计有很多弊端。

1)容易造成参数组合爆炸,因为对象的属性有很多,一个参数组合列表不定,两个参数组合列表不定,三个四个组合列表等等若此类推,理论上有人脑处理不能的组合,也就是很难适应变化,你只能不断地加入重载功能到服务层接口里面,并且会出现很多类似的代码段,果断后期悲剧。

伪代码实现:

Order Model

class Order
{
public int id{get;set;}
public string Name{get;set;}
public string Number{get;set;}
  public string Text{get;set;}
public float Money{get;set;}
public DateTime CreateDate{get;set;}
}

按照第一种思路具体对Service功能的实现

void EditOrder(int id,string orderName,string orderText,float Money)
{
Order order
=this._OrderRepository.find(id);
order.Name
=orderName;
order.Text
=orderText;
order.Money
=Money;
this._OrderRepository.save(order);
}
  

2)使用简单类型作为参数列表,在一定数量级的情况下,会出现重载冲突,如

void EditOrder(int id,string orderName,string orderText,float Money);

void EditOrder(int id,string orderNumber,string orderTitle,float Money);

显然这个重载方法会有编译错误,为了迎合这种需求,你只能通过修改方法名字来满足,十分别扭。以下是修改了的代码

void EditOrder(int id,string orderName,string orderText,float Money)
{
Order order
=this._OrderRepository.find(id);
order.Name
=orderName;
order.Text
=orderText;
order.Money
=Money;
this._OrderRepository.save(order);
}
void EditOrderNum(int id,string orderNumber,string orderText,float Money)
{
Order order
=this._OrderRepository.find(id);
order.Number
=orderNumber; //把Name改成Number属性修改
order.Text=orderText;
order.Money
=Money;
this._OrderRepository.save(order);
}

从这个修改结果可以看出,存在很多重复代码段,都是类似代码,即不能复用,也不能不写,而且当需求不断变化,这个服务层就越来越厚,也是上述文章提到的弊端之一。

 

第二种:直接使用领域对象作为数据载体,好处是能够适应所有围绕领域模型的需求变化,解决了第一种情况不断重载的局面,也随理成章地解决了重载冲突的局面。然而这里存在一些弊端。

1)由于数据载体是一个领域对象,调用端传送回来的数据可能只有几个属性,也就是很可能存在大量的null值和默认值的属性,面对这种情况,我们要在功能实现里面对对象属性进行筛选验证,当然只有几个属性这不能当一回事,但领域对象大起上来,有几十个属性或以上,这部分工作就变得痛苦起来了。

按照第二种思路对Service功能的实现

void EditOrder(Order data)
{
if(data.id!=0)
{
Order order
=this._OrderRepository.find(data.id);
if(!string.IsNullOrEmpty(data.Name))
{
order.Name
=data.Name;
}
if(data.CreateDate!=new DateTime())
{
order.CreateDate
=data.CreateDate;
}
this._OrderRepository.save(order);
}
throw new Exception("Error");
}

要注意的是,这里的Order data参数,只充当一个数据载体,而不是一个领域对象。

第三种:使用DTO代替领域对象作为数据载体,其实我也用过这种做法,但我找不到其好处,引用别人的观点就是,隔离了领域对象对UI的影响,安全性得到保证,从我的实践经验来看,确实是把领域对象隔离出UI,但换来的是大量的DTO管理和适配问题,这些问题比维护问题还头痛,如果是单纯从分布式的角度来解决问题,结合第二种和第三种做法可以产生一变种,前端返回给后台的数据载体用领域对象,而前端接收的数据载体用DTO。

结论:第二种做法是暂时面对非分布式开发是下比较好的做法,但总感觉有点别扭,所以把问题拿出来,集思广益!希望大家积极参与!我相信你们的经验中一定有更好的解决办法。