Как создать классовую зависимость, пытаясь избежать закона Деметры

Хорошо, я искал и не смог найти подходящего решения для своей проблемы, я переделываю часть нашей системы торговых точек. Предположим, у нас есть следующие классы:

TWorkShift = class
   Date: TDateTime;
   fTotalSold: Currency;
   fSales: TList<TSale>; 
public
   property TotalSold: Currency read fTotalSold write fTotalSold;
   property Sales: Currency read fSales write fSales;
end;

TSale = class
    fAmount: Currency;
    fWorkShift: TWorkShift;
public
    property Amount: Currency read fAmount write fAmount; 
    procedure Save;  
end;

Теперь проблема, с которой я сталкиваюсь, состоит в том, чтобы попытаться придумать лучшую идею, не нарушая Закон Деметры. Я пытаюсь сделать следующее:

  1. Каждый раз, когда новый TSale сохраняется, я хочу добавить его в список продаж TWorkShift текущего пользователя, а также я хочу суммировать сумму продажи в "TotalSold" TWorkShift.

Я пробовал два разных подхода:

Подход А:

// Предположим, у нас есть рабочая смена с ID 1 и она загружается из базы данных с помощью: CurrentShift:= TWorkShift.Create(1);

NewSale := TSale.Create;
NewSale.Amount:=100;
NewSale.Save;

CurrentShift.Sales.Add(NewSale);
CurrentShift.TotalSold := CurrentShift.TotalSold + NewSale.Amount;

Проблема этого подхода в том, что его сложно проверить, потому что я хочу инкапсулировать логику суммы в некоторых классах или где-то еще (возможно, новый класс?).

Подход Б:

Мой другой подход - включить этот код в сам класс TSale:

procedure TSale.Save;
begin
    SaveToDataBase; 

    fWorkShift.Sales.Add(Self);
    fWorkShift.TotalSold := fWorkShift.TotalSold + Self.Amount;
end;

Я думаю, что такой подход нарушает закон Деметры и не подходит мне.

Я хочу найти "правильный путь", чтобы сделать его максимально простым и легким в обслуживании в будущем. Так что любые предложения будут оценены.

Спасибо

2 ответа

Решение

Если вы хотите добавить продажу в TWorkShift, то вы должны иметь

TWorkShift.AddSale(aSale: TSale);
begin
  Sales.Add(aSale);
end;

Другими словами, TWorkShift должен "попросить" то, что ему нужно.

Кроме того, я не вижу причин, по которым у TSale будет поле TWorkShift. У Workshift много продаж, но почему у Sale есть WorkShift?

Вы делаете что-то, когда добавляете элементы в TList, чтобы вы могли использовать OnNotify. Я не знаю, использует ли Aurelius это событие, поэтому я добавил для этого некоторый код. Вы только должны увидеть, может ли назначение OnNotify происходить внутри платформы после того, как список назначен вашему объекту TWorkShift, потому что тогда он может перезаписать обработчик события NotifySales.

type
  TWorkShift = class
  private
    Date: TDateTime;
    fTotalSold: Currency;
    fSales: TList<TSale>;
    fNotifySales: TCollectionNotifyEvent<TSale>;
    procedure NotifySales(Sender: TObject; const Item: TSale;
      Action: TCollectionNotification);
    procedure SetSales(const Value: TList<TSale>);
  public
    property TotalSold: Currency read fTotalSold write fTotalSold;
    property Sales: TList<TSale> read fSales write SetSales;
  end;

procedure TWorkShift.NotifySales(Sender: TObject; const Item: TSale;
  Action: TCollectionNotification);
begin
  if Assigned(fNotifySales) then
    fNotifySales(Sender, Item, Action);

  case Action of
    cnAdded: fTotalSold := fTotalSold + Item.Amount;
    cnRemoved: fTotalSold := fTotalSold - Item.Amount;
  end;
end;

procedure TWorkShift.SetSales(const Value: TList<TSale>);
begin
  if Assigned(fSales) then
  begin
    fSales.OnNotify := fNotifySales;
    fNotifySales := nil;
  end;

  fSales := Value;

  if Assigned(fSales) then
  begin
    fNotifySales := fSales.OnNotify;
    fSales.OnNotify := NotifySales;
  end;
end;
Другие вопросы по тегам