跨对象边界的信息泄漏


10

很多时候,我的业务对象往往会遇到信息需要经常跨越对象边界的情况。在进行OO时,我们希望信息位于一个对象中,并且处理该信息的所有代码应尽可能在该对象中。但是,业务规则没有遵循该原则,这给我带来了麻烦。

作为示例,假设我们有一个包含多个OrderItems的Order,该OrderItems引用一个具有价格的InventoryItem。我调用了Order.GetTotal(),它对OrderItem.GetPrice()的结果求和,该结果乘以InventoryItem.GetPrice()乘以数量。到目前为止,一切都很好。

但随后我们发现有些商品以一交易两卖。我们可以通过让OrderItem.GetPrice()做类似InventoryItem.GetPrice(quantage)的事情并让InventoryItem处理来解决这个问题。

但是,然后我们发现二对一交易仅持续特定时间段。该时间段必须基于订单日期。现在我们将OrderItem.GetPrice()更改为InventoryItem.GetPrice(quatity,order.GetDate())

但是然后我们需要根据客户在系统中待了多长时间来支持不同的价格:InventoryItem.GetPrice(数量,order.GetDate(),order.GetCustomer())

但是,事实证明,一对一交易不仅适用于购买多个相同库存物品,而且适用于InventoryCategory中任何物品的多个交易。在这一点上,我们举起手来,只给InventoryItem订购项,并使其通过访问器在对象引用图上移动,以获取其需求的信息:InventoryItem.GetPrice(this)

TL; DR我希望对象之间的耦合度低,但是业务规则经常迫使我从各地访问信息以做出特定决策。

有好的技巧可以解决这个问题吗?其他人也会发现同样的问题吗?


4
乍一看,似乎InventoryItem类试图通过计算价格来做太多事情,返回给定价格是一回事,但是销售价格和特殊业务规则不应在InventoryItem中处理。推出用于计算价格的类,并使其处理来自订单,库存和客户等数据的需求。
克里斯(Chris

@Chris,是的,拆分定价逻辑可能是一个好主意。我的问题是,这样的对象将与所有与订单相关的对象紧密耦合。那是困扰我的部分,也是我想知道是否可以避免的部分。
Winston Ewert

1
如果我什么都不想称呼它,那么需要所有这些信息,那么它就必须以某种方式消耗这些信息以产生期望的结果。如果您已经建立了库存,物料,客户等,则在自己的区域中实施业务逻辑最有意义。我真的没有更好的解决方案。
克里斯,

Answers:


6

我们在工作时基本具有相同的经验,并且通过使用OrderBusinessLogic类解决了这一问题。在大多数情况下,您描述的布局都适用于我们大部分业务。很好,干净,简单。但是,在您购买了该类别中的任何2种商品的情况下,我们将其视为“业务执行”,并让OrderBL类通过遍历所需的对象来重新计算总数。

这是一个完美的解决方案吗?我们仍然有一个类对其他类了解得太多,但是至少我们已经将这种需求从业务对象中转移到了业务逻辑类中。


2
如有疑问,请添加其他课程。
克里斯,

1

像你这样的声音需要一个单独的折扣对象(或它们的列表),保持的所有的东西的轨道,然后应用折扣(县)订单,类似Order.getTotal(Discount)Discount.applyTo(Order)或相似。


我可以看到将代码放在Discount而不是Inventory对象的位置会更好。但是,折扣对象似乎仍然必须访问所有不同对象的信息才能完成其工作。因此,至少就我所知,这并不能解决问题。
温斯顿·埃韦特

2
我认为Discount对象是个好主意,但我可以使它实现观察者模式(en.wikipedia.org/wiki/Observer_pattern),并且Discounts作为模式的主题
BlackICE

1

完全可以访问其他类的数据。但是,您希望这是一种单向关系。例如,假设ClassOrder访问CallItem。理想情况下,ClassItem不应访问ClassOrder。我认为您在订单类中缺少的是某种业务逻辑,可能会也可能不会保证像Walter建议的那样。

编辑:回应温斯顿的评论

我认为您根本不需要存货项目对象……至少在使用它的方式上。我将改用一个库存类来管理库存数据库。

我将通过ID来参考库存商品。每个订单将包含一个清单ID和相应数量的列表。

然后,我将用类似这样的方法计算订单总数。
Inventory.GetCost(商品,客户名称,日期)

然后,您可以具有其他帮助程序功能,例如:

Inventory.ItemsLefts(int itemID)
Inventory.AddItem(int itemID,int数量)
Inventory.RemoveItem(int itemID,int数量)
Inventory.AddBusinessRule(...)
Inventory.DeleteBusinessRule(...)


从紧密相关的类中请求数据完全可以。当我从间接相关的类访问数据时,就会出现问题。要在Order类中实现该逻辑,将要求Order类直接与Inventory对象(包含必需的定价数据)进行处理。那是困扰我的部分,因为它将Order耦合到(理想情况下)它一无所知的类。
温斯顿·埃韦特

本质上,您的想法是消除OrderItem,而Order会跟踪所有这些信息本身。然后很容易将该信息传递给另一个对象以获取价格信息。那行得通。(在特定的场景中,该示例基于OrderItem过于松散,确实确实需要成为其自己的对象)
Winston Ewert

对我来说没有意义的是为什么您希望使用ID号而不是对象引用来引用库存。对我来说,跟踪对象引用和数量而不是项目ID和引用似乎更好。
温斯顿·埃韦特

我根本没有建议。我认为您仍应具有订购商品的类或结构。我只是不认为单个库存项目对象应该包含价格,主要是因为我不确定在不要求某种数据库的情况下如何获得价格。订单或库存项目严格来说是包含项目标识符和数量的数据类。订单本身将包含这些对象的列表。
Pemdas 2011年

我不确定在第二条评论中您要问什么。并非所有事物都必须是对象。我真的不确定您如何在没有某种ID的情况下找到特定的物品。
Pemdas 2011年

0

经过更多考虑之后,我提出了自己的替代策略。

定义价格类。Inventory.GetPrice()返回价格对象

Price OrderItem::GetPrice()
{
    return inventory.GetPrice().quantity(quantity);
}

Currency OrderItem::GetTotal()
{
    Price price = empty_price();
    for(item in order_items)
    {
         price = price.combine( item.GetPrice() )
    }
    price = price.date(date).customer(customer);
    return price.GetActualPrice();
}

现在,Price类(以及可能的一些相关类)封装了定价逻辑,而定单则不必担心。Price对Order / OrderItem一无所知,只是简单地将信息输入其中。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.