代码改变世界

一个小模型的讨论

2008-09-18 13:53  我的熊  阅读(1976)  评论(10编辑  收藏  举报

 

模型如上图所示,类的关系简单明了,如何把这个模型转化为代码?从这个简单模型分析中,体会如何理解领域关系.如何用代码来表达这种关系.

实现一:在构造函数里指定关联.

class Order{
private Customer customer;
public Order(Customer customer){
this.customer = customer;
}
}

实现二:在方法里指定关联.

class Order{
private Customer customer;
public Order(){}
public void initOrder(Customer customer){
this.customer = costomer;
//其他一些信息
}
}

实现一在订单生成时就指定了顾客,实现二是在下单是随时指定顾客,实现二较灵活,如果有这种需求:顾客可以在不登录的情况下下单,只有在支付时才需要指定该订单的顾客.实现一,也可以进行修改达到实现二的目的.比如订单生成时给它一个空的顾客,在定义一个顾客属性,可以在下单是随时指定顾客,代码如下:

class  Order{
private  Customer customer;
public Order(Customer customer){
this.customer = customer;
 }
public Customer Customer {
    get { return customer; }
     set { customer = value; }
}
}

业务是否合理:

“顾客可以在不登录的情况下下单,只有在支付时才需要指定该订单的顾客”, .听上去没什么问题.

分析利于弊:

顾客在不登陆的情况下就下订单是不太合理的,不指定顾客就下单,那么支付订单的时候顾客怎么去挑选他真正想要的订单呢。所以我觉得这个问题只有下单和支付订单两个动作,下单时生成订单并且要指定顾客,这样顾客才能在支付订单的时候很快的定位他自己下的订单。而且我觉得下完订单以后顾客信息是不能随意更改的。所以我感觉实现一是合理的,重构后的实现一和实现二虽然可以让在支付订单的时候随时指定顾客,但它也同时暴露了修改订单客户的接口,使得他在支付完成以后也能更改顾客信息,而这将是非常危险的动作。

 

从OO的角度解释:

一, Order和Customer的关系时怎么样,主要看关联是否紧密,聚合就是整体和部分的关系,没有部分哪有整体?因此,在构造整体时,当然必须首先构造部分了。就象组装汽车,必须先有轮子这个部分。实现二较灵活,如果有这种需求:顾客可以在不登录的情况下下单,只有在支付时才需要指定该订单的顾客. 这是架构考虑,架构是提供模型运行的计算机环境,因为必须遵从模型的安排和要求,在不违背模型情况下讲究灵活,顾客不登录下订单,这个订单也是有客户的,只是客户是空或不知名,因为他没有登录。不能因为他没有登录,就在构造订单时,不构造客户(哪怕匿名,但是客户是存在的,否则订单不会自己凭空冒出来),这是违反常理的,OO就是做符合常理的事情。

 

二, 对于每一个聚合都会有它的静态契约,那就是每次操作完成必须符合的一个约束。一个对象的操作越多 ,就越容易违反这样的约束。这个模型中订单和顾客是一个强聚合关系,一旦创建就不能随意更改。所以从最小接口原则来讲,我们也不应该对外提供修改顾客的接口。所以实现一是比较好的。

 

罪魁祸首: 偷换概念

再次看看需求:" 顾客可以在不登录的情况下下单,只有在结算时才需要指定该订单的顾客".这里的”下单”到底做如何解释?根据前面的分析中可以提取这个需求的两个动作: “下单-->支付”.其实真正的购物流程是: “挑选商品,并放入购物车 -->下单 -->支付”.正因为我们没有整个流程放一起考虑,造成需求理解的不到位.导致代码不合理. 所以这个需求真正的表达是:"顾客可以在没有登陆的情况下挑选商品并放入购物车,最后要结算时,必须登陆后才能结算”. 现在再来看之前的分析.是完全合理的.原来客户说的”下单”其实是”放入购物车”的意思,现在这个问题很好解决, 购物车里的东西只要谁登陆谁就可以去结算. 所以”实现一”是合理的.

 

总结:

1,一般我们领域建模都是抓出聚合关系,也就是更紧密的关联.

2,把类的关系转化代码时,必须符合类的静态契约.

3, 客户提的需求并不一定都是合理的,有时候他提出来你软件要怎么怎么做,但实际需求说到底是为了解决业务问题,要正确地分析客户的需求,提取实质业务需求,

4, 记住”OO就是做符合常理的事情”。