2010-01-28 48 views
5

Tôi có câu hỏi về việc thực thi quy tắc kinh doanh thông qua mẫu đặc điểm kỹ thuật. Hãy xem xét ví dụ sau:Trợ giúp triển khai mẫu thông số kỹ thuật

public class Parent 
{ 
    private ICollection<Child> children; 

    public ReadOnlyCollection Children { get; } 

    public void AddChild(Child child) 
    { 
     child.Parent = this; 
     children.Add(child); 
    } 
} 


public class Child 
{ 
    internal Parent Parent 
    { 
     get; 
     set; 
    } 

    public DateTime ValidFrom; 
    public DateTime ValidTo; 

    public Child() 
    { 
    } 
} 

Quy tắc kinh doanh phải thực thi rằng không thể có trẻ trong bộ sưu tập có thời hạn hiệu lực giao cắt với nhau.

Vì vậy, tôi muốn thực hiện một đặc tả sau đó được sử dụng để ném ngoại lệ nếu một đứa trẻ không hợp lệ được thêm VÀ cũng có thể được sử dụng để kiểm tra xem quy tắc có bị vi phạm TRƯỚC KHI thêm con hay không.

Giống như:


public class ChildValiditySpecification 
{ 
    bool IsSatisfiedBy(Child child) 
    { 
     return child.Parent.Children.Where(<validityIntersectsCondition here>).Count > 0; 
    } 
} 

Nhưng trong ví dụ này đứa trẻ truy cập vào cha mẹ. Và với tôi điều đó dường như không chính xác. Phụ huynh đó có thể không tồn tại khi đứa trẻ chưa được thêm vào phụ huynh. Làm thế nào bạn sẽ thực hiện nó?

Trả lời

6
public class Parent { 
    private List<Child> children; 

    public ICollection<Child> Children { 
    get { return children.AsReadOnly(); } 
    } 

    public void AddChild(Child child) { 
    if (!child.IsSatisfiedBy(this)) throw new Exception(); 
    child.Parent = this; 
    children.Add(child); 
    } 
} 

public class Child { 
    internal Parent Parent { get; set; } 

    public DateTime ValidFrom; 
    public DateTime ValidTo; 

    public bool IsSatisfiedBy(Parent parent) { // can also be used before calling parent.AddChild 
    return parent.Children.All(c => !Overlaps(c)); 
    } 

    bool Overlaps(Child c) { 
    return ValidFrom <= c.ValidTo && c.ValidFrom <= ValidTo; 
    } 
} 

UPDATE:

Nhưng tất nhiên, sức mạnh thực sự của mô hình đặc điểm kỹ thuật là khi bạn có thể cắm vào và kết hợp quy tắc khác nhau. Bạn có thể có một giao diện như thế này (có thể với một tên tốt hơn):

public interface ISpecification { 
    bool IsSatisfiedBy(Parent parent, Child candidate); 
} 

Và sau đó sử dụng nó như thế này trên Parent:

public class Parent { 
    List<Child> children = new List<Child>(); 
    ISpecification childValiditySpec; 
    public Parent(ISpecification childValiditySpec) { 
    this.childValiditySpec = childValiditySpec; 
    } 
    public ICollection<Child> Children { 
    get { return children.AsReadOnly(); } 
    } 
    public bool IsSatisfiedBy(Child child) { 
    return childValiditySpec.IsSatisfiedBy(this, child); 
    } 
    public void AddChild(Child child) { 
    if (!IsSatisfiedBy(child)) throw new Exception(); 
    child.Parent = this; 
    children.Add(child); 
    } 
} 

Child sẽ là đơn giản:

public class Child { 
    internal Parent Parent { get; set; } 
    public DateTime ValidFrom; 
    public DateTime ValidTo; 
} 

Và bạn có thể triển khai nhiều thông số kỹ thuật hoặc thông số kỹ thuật tổng hợp. Đây là một từ ví dụ của bạn:

public class NonOverlappingChildSpec : ISpecification { 
    public bool IsSatisfiedBy(Parent parent, Child candidate) { 
    return parent.Children.All(child => !Overlaps(child, candidate)); 
    } 
    bool Overlaps(Child c1, Child c2) { 
    return c1.ValidFrom <= c2.ValidTo && c2.ValidFrom <= c1.ValidTo; 
    } 
} 

Lưu ý rằng nó có ý nghĩa hơn để làm cho Child của dữ liệu công cộng không thay đổi (chỉ thiết lập thông qua các nhà xây dựng) để không có trường hợp có thể có dữ liệu của nó thay đổi theo một cách mà có thể làm mất hiệu lực số Parent.

Ngoài ra, hãy xem xét đóng gói phạm vi ngày trong một specialized abstraction.

0

Bạn không có câu lệnh If để kiểm tra xem phụ huynh có không phải là không và nếu có thì trả về false?

+0

Đó có thể là một khả năng. Nhưng tôi chỉ tự hỏi nếu tôi đang sử dụng mô hình này đúng cách ... Sẽ không có hiệu lực là duy nhất khi không có cha mẹ? – Chris

2

Tôi nghĩ rằng phụ huynh có lẽ nên thực hiện xác thực. Vì vậy, trong phụ huynh bạn có thể có một phương thức canBeParentOf (Child). Phương thức này cũng sẽ được gọi ở đầu phương thức AddChild của bạn - sau đó phương thức addChild ném một ngoại lệ nếu canBeParentOf không thành công, nhưng chính bản thân canBeParentOf không ném một ngoại lệ.

Bây giờ, nếu bạn muốn sử dụng các lớp "Validator" để triển khai canBeParentOf, điều đó thật tuyệt vời. Bạn có thể có một phương thức như validator.validateRelationship (Parent, Child). Sau đó, bất kỳ phụ huynh nào cũng có thể giữ một bộ sưu tập các trình xác nhận hợp lệ để có thể có nhiều điều kiện ngăn cản mối quan hệ cha/con. canBeParentOf sẽ chỉ lặp qua các trình duyệt tính hợp lệ để gọi từng phần tử cho con được thêm vào - như trong validator.canBeParentOf (this, child); - bất kỳ false nào cũng sẽ gây ra canBeParentOf trả về false.

Nếu các điều kiện để xác thực luôn giống nhau đối với mọi phụ huynh/trẻ em có thể, thì chúng có thể được mã hóa trực tiếp vào canBeParentOf hoặc bộ sưu tập trình xác thực có thể là tĩnh.

An sang một bên: Liên kết ngược từ con này sang cha khác có thể được thay đổi để chỉ có thể đặt một lần (cuộc gọi thứ hai đến tập hợp sẽ ném ngoại lệ). Điều này sẽ A) Ngăn con bạn vào trạng thái không hợp lệ sau khi nó được thêm vào và B) phát hiện một nỗ lực để thêm nó vào hai cha mẹ khác nhau. Nói cách khác: Làm cho đối tượng của bạn càng gần với bất biến càng tốt. (Trừ khi thay đổi nó cho cha mẹ khác nhau là có thể). Việc thêm trẻ em vào nhiều cha mẹ rõ ràng là không thể (từ mô hình dữ liệu của bạn)

0

Bạn đang cố gắng bảo vệ chống lại Child đang ở trạng thái không hợp lệ.Hoặc

  • sử dụng mô hình xây dựng để tạo ra phổ biến đầy đủ Parent loại để mọi thứ bạn tiếp xúc với người tiêu dùng luôn ở trong tình trạng hợp lệ
  • loại bỏ các tham chiếu đến Parent hoàn toàn
  • Parent tạo tất cả các trường Child vì vậy đây không bao giờ có thể xảy ra

Các trường hợp sau có thể trông (một cái gì đó) như thế này (trong Java):

012.
public class DateRangeHolder { 
    private final NavigableSet<DateRange> ranges = new TreeSet<DateRange>(); 

    public void add(Date from, Date to) { 
    DateRange range = new DateRange(this, from, to); 
    if (ranges.contains(range)) throw new IllegalArgumentException(); 
    DateRange lower = ranges.lower(range); 
    validate(range, lower); 
    validate(range, ranges.higher(lower == null ? range : lower)); 
    ranges.add(range); 
    } 

    private void validate(DateRange range, DateRange against) { 
    if (against != null && range.intersects(against)) { 
     throw new IllegalArgumentException(); 
    } 
    } 

    public static class DateRange implements Comparable<DateRange> { 
    // implementation elided 
    } 
} 
Các vấn đề liên quan