Как создать классовую зависимость, пытаясь избежать закона Деметры
Хорошо, я искал и не смог найти подходящего решения для своей проблемы, я переделываю часть нашей системы торговых точек. Предположим, у нас есть следующие классы:
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;
Теперь проблема, с которой я сталкиваюсь, состоит в том, чтобы попытаться придумать лучшую идею, не нарушая Закон Деметры. Я пытаюсь сделать следующее:
- Каждый раз, когда новый 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;