如何设计类依赖试图避免得墨忒耳定律

时间:2012-04-26 23:23:42

标签: delphi law-of-demeter

好的,我搜索过并找不到适合我问题的解决方案,我正在重新设计销售点系统的一部分。假设我们有以下类:

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的Sales列表中,并且我还想将销售额加到TWorkShift的“TotalSold”中。
  2. 我尝试了两种不同的方法:

    方法A:

    //假设我们有一个ID为1的工作班次,并从数据库加载: CurrentShift:= TWorkShift.Create(1);

    NewSale := TSale.Create;
    NewSale.Amount:=100;
    NewSale.Save;
    
    CurrentShift.Sales.Add(NewSale);
    CurrentShift.TotalSold := CurrentShift.TotalSold + NewSale.Amount;
    

    这种方法的问题在于难以测试,因为我想在一些类或其他地方封装总和的逻辑(可能是一个新类?)。

    方法B:

    我的另一种方法是在TSale类本身中包含该代码:

    procedure TSale.Save;
    begin
        SaveToDataBase; 
    
        fWorkShift.Sales.Add(Self);
        fWorkShift.TotalSold := fWorkShift.TotalSold + Self.Amount;
    end;
    

    我认为这种做法违反了得墨忒耳法,对我来说并不合适。

    我希望找到一种“正确的方法”,以便在将来最大限度地提高代码简单性并易于维护。所以任何建议都会受到赞赏。

    由于

2 个答案:

答案 0 :(得分:3)

如果您想向TWorkShift添加销售,那么您应该

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

换句话说,TWorkShift应该&#34;问&#34;对于它需要的东西。

另外,我没有看到TSale有任何TWorkShift字段的原因。 Workshift有很多销售,但为什么Sale会有WorkShift?

答案 1 :(得分:0)

当您向TList添加项目时,您正在执行某些操作,因此您可以使用OnNotify。 我不知道Aurelius是否也在使用该事件,因此我添加了一些代码。您只需要查看在将列表分配给TWorkShift对象后是否可以在框架内进行分配OnNotify,因为它可能会覆盖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;