Мой текущий проект разделен на несколько классов, которые правильно представляют то, как мы думаем о сущностях в системе. Однако это требует снижения производительности, поэтому я пытаюсь найти способ повышения производительности при сохранении существующей структуры классов.
Я думаю, что пример лучше всего проиллюстрирует проблему: допустим, у меня есть класс Order
, который содержит несколько Item
с. Наивная реализация для доставки Order
отправляет все свои Item
s:
class Order
{
private List<Item> m_items;
public void Ship()
{
foreach (Item item in m_items)
{
item.Ship();
}
}
}
Доставка Item
- это многоэтапный процесс:
class Item
{
public void Ship()
{
var receipt = m_boat.ShipToDestination(this);
ProcessReceipt(receipt);
}
}
Как оказалось, доставка Item
на Boat
занимает много времени. К счастью, Boat
может отправлять несколько Item
одновременно (List<Receipt> Boat.ShipToDestination(List<Item> items)
). Однако, чтобы использовать этот метод, мне нужно будет переписать Order.Ship()
следующим образом:
class Order
{
public void Ship()
{
var receipts = m_boat.ShipToDestination(m_items);
foreach (Receipt receipt in receipts)
{
ProcessReceipt(receipt);
}
}
}
Это означает, что ProcessReceipt
теперь является частью Order
(а не Item
), а также что Item
больше не заботится обо всей своей логистике доставки.
В моем реальном коде есть много методов с этой проблемой, поэтому эффективное использование этого решения означает, что большая часть логики Item
перейдет в класс Order
. Тем не менее, это кажется неправильным местом для этой логики - она действительно должна быть в Item
.
Я подумал об использовании нескольких потоков (например, Task
на Item
), но это кажется слишком расточительным, тем более что Order
может иметь десятки Items
.
Другой подход, который я рассмотрел, - это создание QueuedBoat
с помощью метода QueueForShipping(Item item)
и метода ShipQueuedItems()
. Теперь Order
может звонить ShipQueuedItems
, а Item
может звонить QueueForShipping
. Проблема с этим подходом заключается в том, что в моем реальном коде Receipt
возвращается только после доставки Item
, поэтому код должен выглядеть следующим образом:
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));
}
}
Такое ощущение, что он в правильном направлении (поскольку Order
знает, сколько у него Item
s, это должен быть компонент, который знает, когда все находится на Boat
), но он разбил Ship
на два этапа. В моем реальном коде у меня есть несколько этапов (каждый может быть векторизован отдельно), поэтому метод будет разбит на 3-4 части, что не очень приятно.
Может кто-нибудь предложить способ векторизации Item.Ship()
при сохранении бизнес-логики внутри класса Item
, или мне придется отказаться от разделения Order / Item, чтобы воспользоваться преимуществами векторизации?
Edit:
Другой подход может быть таким:
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);
}
}
Где класс QueuedBoat
вызывает обратный вызов для каждого элемента со своим Receipt
.