How to design class dependency trying to avoid Law of Demeter

delphi, law-of-demeter

Solution

If you want to add a sale to TWorkShift, then you should have

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

In other words, TWorkShift should "ask" for the thing it needs.

Also, I don't see any reason that TSale would have a TWorkShift field. A Workshift has many sales, but why would a Sale have a WorkShift?

Problem

Ok,I´ve searched and couldn´t find a suitable solution for my problem, I am redesigning a part of our point of sale system. Let´s suppose we have the following classes: ``` 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; ``` Now, the problem I am facing is trying to come to the best idea without violating the Law of Demeter. What I am trying to accomplish is the following: - Every time a new TSale is saved I want to add it to the Sales list of the TWorkShift of the current user, and also I want to sum the amount of the sale to the "TotalSold" of the TWorkShift. I´ve tried two different approaches: Approach A: // Let´s suppose we have a working shift with the ID 1 and gets loaded from database with: CurrentShift := TWorkShift.Create(1); ``` NewSale := TSale.Create; NewSale.Amount:=100; NewSale.Save; CurrentShift.Sales.Add(NewSale); CurrentShift.TotalSold := CurrentShift.TotalSold + NewSale.Amount; ``` The problem with this approach is that It´s difficult to test, because I want to encapsulate the logic of the sum in some of the classes or somewhere else (a new class maybe?). Approach B: My other approach is including that code inside the TSale class itself: ``` procedure TSale.Save; begin SaveToDataBase; fWorkShift.Sales.Add(Self); fWorkShift.TotalSold := fWorkShift.TotalSold + Self.Amount; end; ``` This approach I think violates the Law of Demeter and doesn´t feel right to me. I Want to find a "right way" to do it maximizing code simplicity and easy of maintenance in the future. So any suggestions would be appreciated. Thanks

Original source