2008-09-24 27 views
5

इस तरह के कठिन समझाने के लिए है, मुझे आशा है कि मेरी अंग्रेजी के लिए पर्याप्त है:नियंत्रण का उपयोग - पैटर्न की आवश्यकता है

मैं एक वर्ग 'ए' जो "वर्ग की वस्तुओं की एक सूची बनाए रखना चाहिए है बी "(एक निजी सूची की तरह)। वर्ग "ए" का उपभोक्ता सूची में आइटम जोड़ने में सक्षम होना चाहिए। सूची में आइटम जोड़े जाने के बाद, उपभोक्ता को फिर से उन्हें संशोधित करने में सक्षम नहीं होना चाहिए, अकेले छोड़ दिया जाना चाहिए कि वह सूची के साथ खुद को गुस्सा नहीं कर पाएगा (आइटम जोड़ें या हटाएं)। लेकिन वह सूची में वस्तुओं की गणना करने और उनके मूल्य प्राप्त करने में सक्षम होना चाहिए। क्या इसके लिए कोई पैटर्न है? आप वह कैसे करेंगें?

यदि प्रश्न पर्याप्त स्पष्ट नहीं है, तो कृपया मुझे बताएं।

उत्तर

3

सूची या उसके आइटम को संपादित करने से रोकने के लिए आपको उन्हें immutable बनाना है, जिसका अर्थ है कि आपको प्रत्येक अनुरोध पर किसी तत्व का एक नया उदाहरण वापस करना होगा। "सी # में अचल स्थिति" के

देखें एरिक Lippert उत्तम श्रृंखला: http://blogs.msdn.com/ericlippert/archive/tags/Immutability/C_2300_/default.aspx (आप थोड़ा नीचे स्क्रॉल करना)

+0

ग्रेट लिंक - –

+0

के लिए धन्यवाद अपरिवर्तनीयता एरिक इस बारे में बात कर रही है इसका मतलब यह नहीं है कि हर बार एक नया उदाहरण लौटाया जाए। वह आपको दिखाता है कि वस्तुओं को कैसे लिखना है, एक बार उनका निर्माण हो जाने के बाद, कभी नहीं बदला। इसका मतलब है कि आप हर जगह एक ही घटना साझा कर सकते हैं और आश्वासन दिया जा सकता है कि कोई और उस घटना को नहीं बदलेगा। –

0

संपादित करें: संस्करण संदर्भों के लिए समर्थन जोड़ा गया। कॉलर केवल संस्करण संदर्भ के अंदर तत्व जोड़ सकता है। आप आमतौर पर लागू कर सकते हैं कि उदाहरण के जीवनकाल के लिए केवल एक संस्करण संदर्भ बनाया जा सकता है।


encapsulation का उपयोग करके आप आंतरिक निजी सदस्य तक पहुंचने के लिए नीतियों के किसी भी सेट को परिभाषित कर सकते हैं।

namespace ConsoleApplication2 
{ 
    using System; 
    using System.Collections.Generic; 
    using System.Collections; 

    class B 
    { 
    } 

    interface IEditable 
    { 
     void StartEdit(); 
     void StopEdit(); 
    } 

    class EditContext<T> : IDisposable where T : IEditable 
    { 
     private T parent; 

     public EditContext(T parent) 
     { 
      parent.StartEdit(); 
      this.parent = parent; 
     } 

     public void Dispose() 
     { 
      this.parent.StopEdit(); 
     } 
    } 

    class A : IEnumerable<B>, IEditable 
    { 
     private List<B> _myList = new List<B>(); 
     private bool editable; 

     public void Add(B o) 
     { 
      if (!editable) 
      { 
       throw new NotSupportedException(); 
      } 
      _myList.Add(o); 
     } 

     public EditContext<A> ForEdition() 
     { 
      return new EditContext<A>(this); 
     } 

     public IEnumerator<B> GetEnumerator() 
     { 
      return _myList.GetEnumerator(); 
     } 

     IEnumerator IEnumerable.GetEnumerator() 
     { 
      return this.GetEnumerator(); 
     } 

     public void StartEdit() 
     { 
      this.editable = true; 
     } 

     public void StopEdit() 
     { 
      this.editable = false; 
     } 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      A a = new A(); 
      using (EditContext<A> edit = a.ForEdition()) 
      { 
       a.Add(new B()); 
       a.Add(new B()); 
      } 

      foreach (B o in a) 
      { 
       Console.WriteLine(o.GetType().ToString()); 
      } 

      a.Add(new B()); 

      Console.ReadLine(); 
     } 
    } 
} 
+0

यह एक अच्छा कार्यान्वयन है, लेकिन मुझे नहीं लगता कि ओ में mutators तक पहुंच लूप के लिए आपके उदाहरण में नियंत्रित है। –

+0

मुझे एडिटकॉन्टेक्स्ट अवधारणा पसंद है, लेकिन ए को दो बार संपादित करने से रोकने के लिए यहां कुछ भी नहीं है, शायद ऊपर दिए गए उदाहरण में foreach के दौरान। शायद अगर आप ए के कन्स्ट्रक्टर में संपादन योग्य = सत्य सेट करते हैं, और फिर उस लाइन को StartEdit से हटा दें? –

0

आप मूल रूप से दूर वर्ग बी आइटम के लिए संदर्भ देने के लिए बचना चाहते हैं: निम्न उदाहरण में अपनी आवश्यकताओं की एक बुनियादी कार्यान्वयन है। यही कारण है कि आपको वस्तुओं की एक प्रति करना चाहिए।

मुझे लगता है कि इसे सूची वस्तु के ToArray() विधि के साथ हल किया जा सकता है। यदि आप परिवर्तनों को रोकना चाहते हैं तो आपको सूची की एक गहरी प्रतिलिपि बनाने की आवश्यकता है।

आम तौर पर बोलते हुए: अधिकतर समय अच्छे व्यवहार को लागू करने के लिए एक प्रतिलिपि करना उचित नहीं है, खासकर जब आप उपभोक्ता भी लिखते हैं।

0
public class MyList<T> : IEnumerable<T>{ 

    public MyList(IEnumerable<T> source){ 
     data.AddRange(source); 
    } 

    public IEnumerator<T> GetEnumerator(){ 
     return data.Enumerator(); 
    } 

    private List<T> data = new List<T>(); 
} 

नकारात्मक पक्ष यह है कि एक उपभोक्ता, आइटम यह गणनाकार से हो जाता है संशोधित कर सकते हैं एक है समाधान निजी सूची < टी> की गहरी प्रतिलिपि बनाना है।

0

यह स्पष्ट नहीं था कि आपको सूची में जोड़े जाने के बाद बी को खुद को अपरिवर्तनीय होने की आवश्यकता है या नहीं। आप बी के लिए केवल पढ़ने के लिए इंटरफ़ेस का उपयोग कर एक चाल चला सकते हैं, और केवल सूची के माध्यम से इन्हें उजागर कर सकते हैं।

internal class B : IB 
{ 
    private string someData; 

    public string SomeData 
    { 
     get { return someData; } 
     set { someData = value; } 
    } 
} 

public interface IB 
{ 
    string SomeData { get; } 
} 
+0

यह ठीक है, जहां तक ​​यह जाता है, लेकिन कॉलर बी पर किसी भी पुनर्प्राप्त आईबी इंटरफेस को बी पर ला सकता है, इस प्रकार उन्हें सेटटर का उपयोग करने की इजाजत मिलती है। –

+0

यह सेटअप पर निर्भर करता है। उपरोक्त उदाहरण में, आप कक्षा की दृश्यता को बदलकर अक्सर इसे रोक सकते हैं। वैसे भी, कम से कम यह कार्यान्वयन आपको एक संकेत देता है कि आपको वस्तुओं को अद्यतन नहीं करना है। –

0

सरल है कि मैं के बारे में सोच सकते हैं वापसी अंतर्निहित संग्रह का एक readonly version संपादन नहीं रह गया है की अनुमति दी है, तो है।

public IList ListOfB 
{ 
    get 
    { 
     if (_readOnlyMode) 
      return listOfB.AsReadOnly(); // also use ArrayList.ReadOnly(listOfB); 
     else 
      return listOfB; 
    } 
} 

व्यक्तिगत रूप से हालांकि, मैं ग्राहक के लिए अंतर्निहित सूची का खुलासा नहीं होगा और सिर्फ जोड़कर, निकालकर, और गणना बी उदाहरणों के लिए तरीके प्रदान करते हैं।

+0

यह आवश्यकता को प्राप्त नहीं करता है कि "आइटम सूची में जोड़े जाने के बाद, उपभोक्ता उन्हें फिर से संशोधित करने में सक्षम नहीं होना चाहिए"। नई सूची में मूल सूची में आइटमों के लिए * संदर्भ * शामिल हैं और उपयोगकर्ता इच्छानुसार म्यूटेटर को कॉल कर सकते हैं। –

+0

मैं बेहतर आपके प्रॉक्सीबी वर्ग का उपयोग करता हूं। :) – jop

1

वाह, यहां एक साधारण समस्या के लिए यहां कुछ जटिल जटिल उत्तर दिए गए हैं।

एक निजी List<T>

एक public void AddItem(T item) विधि किया है - जब भी आप उस काम करना बंद कर बनाने के लिए निर्णय लेते हैं, यह काम करना बंद कर सकते हैं। आप एक अपवाद फेंक सकते हैं या आप इसे चुपचाप विफल कर सकते हैं। इस पर निर्भर करता है कि आप वहां क्या चल रहे थे।

वहाँ संग्रह ही अपरिवर्तनीय बनाने के लिए कई तरीके हैं, कि return _theList.ToArray()

+0

एलओएल! मैं मानता हूं, मैंने एक ही सूची में निजी सूची और सार्वजनिक स्ट्रिंग [] के साथ एक ही काम किया था, मैं इस कारण से कल रात कर रहा था :) –

+0

यह आवश्यकता को प्राप्त नहीं करता है कि "आइटम को जोड़े जाने के बाद सूची, उपभोक्ता उन्हें फिर से संशोधित करने में सक्षम नहीं होना चाहिए "। सरणी में सूची में वस्तुओं के लिए * संदर्भ * शामिल हैं और उपयोगकर्ता इच्छानुसार म्यूटेटर को कॉल कर सकते हैं। –

+0

मैं सेब रोज से सहमत हूं: यदि आप नहीं चाहते हैं कि सूची के तत्व संपादन योग्य हों, तो आपको मूल संदर्भ -> अपरिवर्तनीयता वापस करने से रोकना होगा। – VVS

1

के रूप में इन उत्तरों के कई दिखाने करता है एक public T[] GetItems() विधि है।

संग्रह के सदस्यों को अपरिवर्तनीय रखने के लिए और अधिक प्रयास करना पड़ता है। एक संभावना यह (संक्षिप्तता की कमी के लिए माफी) एक मुखौटा/प्रॉक्सी का उपयोग करने के लिए है:

class B 
{ 
    public B(int data) 
    { 
     this.data = data; 
    } 

    public int data 
    { 
     get { return privateData; } 
     set { privateData = value; } 
    } 

    private int privateData; 
} 

class ProxyB 
{ 
    public ProxyB(B b) 
    { 
     actual = b; 
    } 

    public int data 
    { 
     get { return actual.data; } 
    } 

    private B actual; 
} 

class A : IEnumerable<ProxyB> 
{ 
    private List<B> bList = new List<B>(); 

    class ProxyEnumerator : IEnumerator<ProxyB> 
    { 
     private IEnumerator<B> b_enum; 

     public ProxyEnumerator(IEnumerator<B> benum) 
     { 
      b_enum = benum; 
     } 

     public bool MoveNext() 
     { 
      return b_enum.MoveNext(); 
     } 

     public ProxyB Current 
     { 
      get { return new ProxyB(b_enum.Current); } 
     } 

     Object IEnumerator.Current 
     { 
      get { return this.Current; } 
     } 

     public void Reset() 
     { 
      b_enum.Reset(); 
     } 

     public void Dispose() 
     { 
      b_enum.Dispose(); 
     } 
    } 

    public void AddB(B b) { bList.Add(b); } 

    public IEnumerator<ProxyB> GetEnumerator() 
    { 
     return new ProxyEnumerator(bList.GetEnumerator()); 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return this.GetEnumerator(); 
    } 
} 

इस समाधान का नकारात्मक पक्ष यह है कि फोन करने वाले ProxyB वस्तुओं का संग्रह से अधिक बार-बार दोहराना होगा, बजाय बी वस्तुओं है वे जोड़ा।

संबंधित मुद्दे