2012-04-26 33 views
5

Ok, tôi đã tìm kiếm và không thể tìm ra giải pháp phù hợp cho vấn đề của mình, tôi đang thiết kế lại một phần của hệ thống bán hàng của chúng tôi. Giả sử chúng ta có các lớp sau:Làm thế nào để thiết kế sự phụ thuộc của lớp học để tránh Luật 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; 

Bây giờ, vấn đề tôi đang đối mặt đang cố gắng đưa ra ý tưởng tốt nhất mà không vi phạm Luật Demeter. Những gì tôi đang cố gắng để thực hiện như sau:

  1. Mỗi khi một TSale mới được lưu Tôi muốn thêm nó vào danh sách bán hàng của TWorkShift của người sử dụng hiện nay, và tôi cũng muốn tổng số tiền bán cho "TotalSold" của TWorkShift.

Tôi đã thử hai cách tiếp cận khác nhau:

Cách tiếp cận A:

// chúng ta hãy giả sử chúng ta có một sự thay đổi làm việc với ID 1 và được nạp từ cơ sở dữ liệu với: CurrentShift: = TWorkShift.Create (1);

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

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

Vấn đề với phương pháp này là Nó rất khó để kiểm tra, bởi vì tôi muốn để đóng gói logic của tổng trong một số các lớp học hoặc ở một nơi khác (một lớp mới có thể?).

Cách tiếp cận B:

cách tiếp cận khác của tôi là bao gồm cả mã bên trong lớp TSale bản thân:

procedure TSale.Save; 
begin 
    SaveToDataBase; 

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

Cách tiếp cận này tôi cho là vi phạm Luật Demeter và doesn't cảm thấy đúng với tôi .

Tôi muốn tìm một "đúng cách" để làm điều đó tối đa hóa sự đơn giản của mã và dễ bảo trì trong tương lai. Vì vậy, bất kỳ đề xuất sẽ được đánh giá cao.

Cảm ơn

Trả lời

3

Nếu bạn muốn thêm một bán cho TWorkShift, sau đó bạn nên có

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

Nói cách khác, TWorkShift nên "yêu cầu" cho điều nó cần.

Ngoài ra, tôi không thấy bất kỳ lý do nào khiến TSale có trường TWorkShift. Một Workshift có nhiều bán hàng, nhưng tại sao một Sale lại có WorkShift?

+0

Cảm ơn Nick, Vâng trong trường hợp này tôi đang sử dụng khuôn khổ Aurelius ORM vì vậy tôi có một "Hiệp hội" để có được các thông tin như: 'Bán: = Manager.Find (1); ShowMessage ('Bán đã được bán trong ca làm việc với ID:' + IntToStr (Sale.Shift.ID)); ' Điều này là cần thiết vì đôi khi tôi cần hiển thị tất cả thông tin về bán hàng, như thay đổi trong đó bán, ngày, thủ quỹ, v.v. –

+1

Luis, đó là vấn đề đối với lớp trình bày không phải BL. Lớp trình bày nên tập hợp tất cả thông tin cần thiết. Vì vậy, trong trường hợp của bạn, bạn có thể lấy đối tượng bán hàng từ đối tượng tính toán và có tất cả thông tin. – whosrdaddy

+0

Luis - nếu một ORM đang buộc bạn phải làm điều đó, thì có thể bạn cần xem xét sử dụng một ORM khác. Đó là thiết kế tồi tệ - một Bán nên không biết gì về Workshift nơi nó xảy ra. Điều gì sẽ xảy ra nếu bạn muốn bán mọi thứ từ bất kỳ Workshift nào? –

0

Bạn đang làm điều gì đó khi bạn thêm các mục vào TList để bạn có thể sử dụng OnNotify. Tôi không biết nếu Aurelius cũng đang sử dụng sự kiện đó vì vậy tôi đã thêm một số mã cho điều đó. Bạn chỉ phải xem nếu gán OnNotify có thể xảy ra bên trong khung công tác sau khi danh sách được gán cho đối tượng TWorkShift của bạn vì nó có thể ghi đè lên trình xử lý sự kiện 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; 
+0

Cảm ơn Stefan. Tôi đã thử cách tiếp cận của bạn sau khi tôi đăng câu hỏi này và nó hoạt động như ý tôi nghĩ nó phức tạp và tìm cách tiếp cận của Nick đơn giản và dễ hiểu hơn. Cảm ơn câu trả lời của bạn Tôi rất có giá trị cho thử nghiệm của tôi. –

Các vấn đề liên quan