如何处理对象删除时引用的判断

07-03-26 windflaw
往往有这样的逻辑,一个产品如果被订单引用,则产品不能删除。因此删除产品的逻辑必须去检测是否有订单引用了该产品。原本订单模块依赖于产品模块的,但产品模块中却要知道“订单”这个概念。这显然产生了双向依赖的问题,如果再加入一个新的模块引用到产品,就要去修改产品的删除逻辑代码。如果避免双向依赖的话,我们可能会想到观察者模式,通过在删除逻辑前执行PreDeleteProductionEvent的事件,在订单中来实现对产品删除事件的注册。似乎可以解决这个问题,但考虑到对象被引用时删除检测这种情况在系统中是很常见的——你开发的任何业务对象都有可能再新的对象引用,而在你设计这个对象时是未知的。那么都采用观察者模式,好象给设计带来很大的工作量。不知道前辈们是如何处理这个常见的问题的。
         

banq
2007-03-26 16:55
>原本订单模块依赖于产品模块的,但产品模块中却要知道“订单”这个概念。这显然产生了双向依赖的问题

设计模式属于设计细节,前提必须是分析建模完成的情况下,现在你的建模就有问题,依靠设计模式再去解决问题,就很牵强。

“产品”很显然不应该关联“订单”的,避免双向关联,同时也避免双向依赖,一般产生双向依赖,主要分析方法没有采取Evans DDD这样建模方法来实现,将行为和属性混淆在一起,说到底,还是面向围绕数据表分析设计思维习惯造成的。

你现在需要做的是:

1.寻找一个OO的建模方法,重新分析需求,对"产品" "订单"等建模。

2.避免双向关联。

3.围绕模型,采取Model/Service架构来分离行为。

windflaw
2007-03-26 22:05
>“产品”很显然不应该关联“订单”的

我没有说“产品”要关联“订单”。而是在对产品进行删除的类里,必须要对订单进行查询,才能知道是否有订单引用到要删除的产品,这是客观存在的双向关联,难道这是“需求分析”的错误吗?现在的目的就是为了避免模块间的这种双向关联。

例如(不好意思,下面是用C#的代码,意思大家应该能明白吧):

产品模块:

public class Production

{

}

public class ProductionRepository

{

public static int Delete(Production prod)

{

if(OrderRepository.QueryOrderByProduction(prod).Count > 0)

{

//....提示不可以删除

}

//...执行删除

}

}

订单模块:

public class Order

{

public IList OrderItem;

}

public class OrderItem

{

public Production Prod;

public decimal Quantity;

}

public class OrderRepository

{

public IList QueryOrderByProduction(Production prod)

{

// ...查询订购prod的所有订单

}

}

大家可以看出,产品模块和订单模块,应该是订单模块依赖于产品模块,但是为了要判断是否有订单引用到要删除的产品,则在ProductionRepository.Delete里必须去调用OrderRepository。那么我现在的一种解决办法是:

产品模块:

public class ProductionRepository

{

public static event EventHandle BeforeDelete;

public static int Delete(Production prod)

{

if(BeforeDelete != null)

{

BeforeDelete(this, new ProductionDeleteArgs(prod));

}

}

}

public class ProductionDeleteArgs : EventArgs

{

private Production m_prod;

public ProductionDeleteArgs(Production prod)

{

m_prod = prod;

}

}

订单模块:

public class OrderRepository

{

public OrderRepository()

{

ProductionRepository.BeforeDelete += new EventHandle(this.OnBeforeDeleteProduction);

}

public IList QueryOrderByProduction(Production prod)

{

// ...查询订购prod的所有订单

}

public void OnBeforeDeleteProduction(object sender, EventArgs e)

{

if(OrderRepository.QueryOrderByProduction(prod).Count > 0)

{

//....提示不可以删除

}

}

}

上面用到了C#里的委托的技术,实际上就是一个观察者模式的做法。通过BeforeDelete这个事件委托,在ProductionRepository.Delete里,只需要判断是否有存在删除前的事件,如果有则执行。而OrderRepository则将OnBeforeDeleteProduction注册到BeforeDelete上。这样实现了在删除产品里对订单的引用进行判断。同样的,在系统增加其它的模块的时候,可以采用同样的机制来增加产品删除前的引用判断。而在产品模块就不需反复变更代码。

用“产品模块”和“订单模块”是我想表达我的问题的一个简单例子,并不是我实际工作中的需求。但我想大家应该都会遇上这样的问题,不知道大家是用什么方式来解决的。

>“产品”很显然不应该关联“订单”的,避免双向关联,同时也避免双向依赖,一般产生双向依赖,主要分析方法没有采取Evans DDD这样建模方法来实现,将行为和属性混淆在一起,说到底,还是面向围绕数据表分析设计思维习惯造成的。

请问Banq兄,我是思想里真的有“面向围绕数据表分析设计思维习惯”吗?本身我是最反对用“表分析思维”的,也许是我的思维真的有错,还请指教,谢谢!

banq
2007-03-27 11:53
>是否有订单引用到要删除的产品,这是客观存在的双向关联,难道这是“需求分析”>的错误吗?现在的目的就是为了避免模块间的这种双向关联。

现在才对你这个案例情况有进一步了解,其实我们陷入了双向关联或双向依赖的误区。

删除产品时,必须了解是否有订单引用到要删除的产品,这个是业务需求,按照Evans DDD,这应该是在领域层实现的,实际上这属于删除的一个约束条件,或者认为是是一条业务规则,或规格specification

在Evans DDD这书中说:业务规则不适合放于实体或值对象,但是也不能移出领域层,可以设计一个“谓词”对象。

由于对你的整个系统设计不清楚,我个人觉得你武断分了订单模块和产品模块,而不是依据领域层和服务层来划分。这个问题已经在下面帖子里讨论:

http://www.jdon.com/article/31216.html

所以,目前看来还是由于你分析需求提炼模型时没有考虑仔细,导致这样问题需要依靠设计模式来解决,但是目前看牵强一些,当然不是不可以。

以上观点仅供参考。

[该贴被banq于2007年03月27日 12:00修改过]

windflaw
2007-03-27 14:24
>由于对你的整个系统设计不清楚,我个人觉得你武断分了订单模块和产品模块,而不是依据领域层和服务层来划分

我的问题是“如何处理对象删除时引用的判断 ”,“订单模块”和“产品模块”只是我用于表述我问题的一个假设。当然也可以举例成“产品管理模块”和“财务管理模块”。

我想软件按业务的功能范围来进行模块的划分,应该和DDD不冲突吧。领域层和服务层只是架构的一个划分,和模块应该不是同一个分类标准吧。我想Eric Evans也有提到模块的概念吧。引Evans DDD(第五章5.5节 P78页):一个优秀的设计模型,它的元素能很好地协同工作,适当地选择模块,将那些紧密联系的模型元素集中到一起。这些高度内聚的对象,具有相关的职责,可以将建模和设计的工作集中在单独一个模块中,从而把复杂度限制在一个范围内,使开发人员更容易处理。….如果你的模型是一本书,那么模块就是这本书的章节…

>在Evans DDD这书中说:业务规则不适合放于实体或值对象,但是也不能移出领域层,可以设计一个“谓词”对象。

我的设计里,也没有把删除逻辑放在实体“Production”里呀。那么就这个谓词对象的代码写的时候,它无法预知道未来会有什么新的对象会引用到要删除的“产品”呢。所以我才引入了观察者模式来改变依赖。如果一个程序员开发一个业务模块的话,这个问题才会上升到模块中来。也就是我可以先开发出“产品管理模块”,再开发出“订单管理模块”或“财务管理模块”,那么在开发“产品模块”时,不知道后面还会有财务管理模块,不知道新的模块中有什么类会产生对产品的引用,导致产品不可以物理删除吧。

> 导致这样问题需要依靠设计模式来解决

呵呵,我开这个贴,目的就是想知道“如何处理对象删除时引用的判断 ”这个问题时,大家是怎么解决的。是否可以把您的做法告知一二呢,谢谢

猜你喜欢
4Go 1 2 3 4 下一页