2010-01-28 15 views
5

मेरी एक विनिर्देश पैटर्न के माध्यम से एक व्यापार नियम लागू करने के बारे में एक प्रश्न है। निम्न उदाहरण पर विचार करें:विशिष्टता पैटर्न कार्यान्वयन

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() 
    { 
    } 
} 

व्यापार नियम लागू होना चाहिए कि वहाँ संग्रह जो वैधता अवधि दूसरे के साथ काटती है में एक बच्चे नहीं हो सकता।

इसके लिए मैं एक विनिर्देश लागू करना चाहता हूं जिसका उपयोग तब किया जाना चाहिए जब कोई अमान्य बच्चा जोड़ा जाता है और साथ ही यह भी जांचने के लिए इस्तेमाल किया जा सकता है कि बच्चे को जोड़ने से पहले नियम का उल्लंघन किया जाएगा या नहीं।

तरह:


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

लेकिन इस उदाहरण में बच्चे माता-पिता तक पहुँचता है। और मेरे लिए यह सही नहीं लगता है। वह माता-पिता मौजूद नहीं हो सकता है जब बच्चे को अभी तक माता-पिता में जोड़ा नहीं गया है। आप इसे कैसे कार्यान्वित करेंगे?

उत्तर

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; 
    } 
} 

अद्यतन:

लेकिन निश्चित रूप से, विनिर्देश पैटर्न की वास्तविक शक्ति है, जब आप में प्लग और अलग नियम जोड़ सकते हैं। आप (एक बेहतर नाम के साथ संभवतः) इस तरह एक अंतरफलक हो सकता है:

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

और फिर 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 आसान होगा:

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

और आप कई विनिर्देशों, या समग्र विनिर्देशों को लागू कर सकते हैं।

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; 
    } 
} 

ध्यान दें कि इसे और अधिक समझ में आता है Child के सार्वजनिक डेटा अपरिवर्तनीय (केवल निर्माता के माध्यम से सेट) ताकि कोई उदाहरण, अपना डाटा एक तरीका है कि होगा में बदल दिया है सकते हैं बनाने के लिए: यह आपके उदाहरण से एक है Parent को अमान्य करें।

इसके अलावा, specialized abstraction में तिथि सीमा को समाहित करने पर विचार करें।

0

क्या आपके पास यह जांचने के लिए कोई कथन नहीं होगा कि माता-पिता शून्य नहीं थे और यदि ऐसा झूठा हो तो?

+0

यह एक संभावना हो सकती है। लेकिन मैं बस सोच रहा हूं कि क्या मैं इस पैटर्न का सही तरीके से उपयोग कर रहा हूं ... क्या माता-पिता होने पर वैधता अद्वितीय नहीं होगी? – Chris

2

मुझे लगता है कि माता-पिता को शायद सत्यापन करना चाहिए। तो माता-पिता में आपके पास एक canBeParentOf (चाइल्ड) विधि हो सकती है। इस विधि को आपके AddChild विधि के शीर्ष पर भी बुलाया जाएगा - तो addBild विधि अपवाद फेंकता है अगर canBeParentOf विफल हो जाता है, लेकिन canBeParentOf स्वयं अपवाद नहीं फेंकता है।

अब, यदि आप canBeParentOf को लागू करने के लिए "वैलिडेटर" कक्षाओं का उपयोग करना चाहते हैं, तो यह शानदार होगा। आपके पास validator.validateRelationship (माता-पिता, बच्चा) जैसी विधि हो सकती है। फिर किसी भी माता-पिता को वैधकर्ताओं का संग्रह हो सकता है ताकि माता-पिता/बाल संबंधों को रोकने में कई स्थितियां हो सकें। canBeParentOf केवल वही वैधताकर्ताओं को फिर से सक्रिय करेगा जो बच्चे को जोड़े जाने के लिए बुलाते हैं - जैसा कि validator.canBeParentOf (यह, बच्चा) में है; - किसी भी झूठे कारण को झूठ वापस करने के लिए कर सकते हैं।

यदि सत्यापन के लिए शर्तें हमेशा हर संभव माता-पिता/बच्चे के लिए समान होती हैं, तो उन्हें या तो सीधे कैनपेरेंटऑफ में कोड किया जा सकता है, या वैधकर्ता संग्रह स्थिर हो सकते हैं।

एक तरफ: बच्चे से माता-पिता के बैक-लिंक को शायद बदला जाना चाहिए ताकि इसे केवल एक बार सेट किया जा सके (सेट को दूसरी कॉल अपवाद फेंकता है)। यह ए) आपके बच्चे को जोड़े जाने के बाद अमान्य स्थिति में आने से रोकें और बी) इसे दो अलग-अलग माता-पिता में जोड़ने का प्रयास करें। दूसरे शब्दों में: अपनी वस्तुओं को यथासंभव अपरिवर्तनीय के करीब बनाएं। (जब तक इसे अलग-अलग माता-पिता में बदलना संभव नहीं है)। एक बच्चे को कई माता-पिता में जोड़ना संभव नहीं है (आपके डेटा मॉडल से)

0

आप Child को अमान्य स्थिति में रखने की कोशिश कर रहे हैं।या तो

  • बिल्डर पैटर्न पूरी तरह से आबादी Parent प्रकार बनाने के लिए इतना है कि सब कुछ आप उपभोक्ता को बेनकाब कर रहा है हमेशा एक मान्य राज्य
  • में उपयोग Parent का संदर्भ हटा दें पूरी तरह से
  • है Parent के सभी उदाहरण बना Child तो यह कभी नहीं हो सकता है

उत्तरार्द्ध मामले लग सकता है इस तरह (कुछ) (जावा में):

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 
    } 
} 
संबंधित मुद्दे