My current project is split into multiple classes that correctly represent the way we think of the entities in the system. However, this is taking a performance price, so I’m trying to come up with a way of improving performance while maintaining the current class structure we have.
I think an example will best illustrate the problem: Let’s say I have an Order class that contains multiple Items. A naive implementation for shipping an Order is shipping all of its Items:
class Order
{
private List<Item> m_items;
public void Ship()
{
foreach (Item item in m_items)
{
item.Ship();
}
}
}
Shipping an Item is a multi-stage process:
class Item
{
public void Ship()
{
var receipt = m_boat.ShipToDestination(this);
ProcessReceipt(receipt);
}
}
As it turns out, shipping the Item on a Boat takes a long time. Luckily, the Boat can ship multiple Items concurrently (List<Receipt> Boat.ShipToDestination(List<Item> items)). However, to use this method I will have to rewrite Order.Ship() as follows:
class Order
{
public void Ship()
{
var receipts = m_boat.ShipToDestination(m_items);
foreach (Receipt receipt in receipts)
{
ProcessReceipt(receipt);
}
}
}
This means that ProcessReceipt is now part of Order (rather than Item), and also that an Item no longer takes care of all of its shipping logistics.
In my real code there are many methods with this problem, so using this solution effectively means that most of the Item logic will move into the Order class. However, this seems like the wrong place to have this logic – it really should be in Item.
I considered using multiple threads (e.g. using a Task per Item) but this seems too wasteful, especially since an Order can have dozens of Items.
Another approach I considered is creating a QueuedBoat with a QueueForShipping(Item item) method and a ShipQueuedItems() method. Now Order can call ShipQueuedItems while Item can call QueueForShipping. The problem with this approach is that in my real code, the Receipt is returned only after the Item was shipped, so the code would have to look like this:
class Order
{
private List<Item> m_items;
public void Ship()
{
foreach (Item item in m_items)
{
item.Ship();
}
m_queuedBoat.ShipQueuedItems();
foreach (Item item in m_items)
{
item.FinalizeShipping();
}
}
}
class Item
{
public void Ship()
{
m_queuedBoat.QueueForShipping(this);
}
public void FinalizeShipping()
{
ProcessReceipt(m_queuedBoat.GetReceiptForItem(this));
}
}
This feels like it’s in the right direction (since Order knows how many Items it has, it must be the component that knows when everything is on the Boat), but it broke Ship into two stages. In my real code I have multiple stages (each can be vectorized separately), so the method would be broken into 3-4 pieces, which is not a pretty sight.
Can someone suggest a way of vectorizing Item.Ship() while keeping the business logic inside the Item class, or will I have to give up on the Order/Item separation in order to take advantage of vectorization?
Edit:
Another approach could be this:
class Order
{
private List<Item> m_items;
public void Ship()
{
for (int i = 0; i < m_items.Count; ++i)
{
bool isLast = (i == (m_items.Count - 1));
item.Ship(isLast);
}
}
}
class Item
{
public void Ship(bool isLast)
{
m_queuedBoat.QueueForShipping(this, FinalizeShipping);
if (isLast) m_queuedBoat.ShipQueuedItems();
}
private void FinalizeShipping(Receipt receipt)
{
ProcessReceipt(receipt);
}
}
Where the QueuedBoat class invokes a callback for each item with its Receipt.
Well, anyway
Shipis a kind of asynchronous operation: there must run several at once. So has to be two-stage:PrepareShipping(void) andPerformShipping(waits for the actual shipping to finish andreturnsprocesses its receipt).I would so something like that:
So the Order can be something like that:
The program can do additional things while shipping is in progress, that is, between
Order.PrepareShipping()andOrder.PerformShipping()(for example, ship out other orders as well, maybe on the same boat).