प्रकार मानचित्रण और विस्तार विधियों के बारे में सर्वोत्तम अभ्यास


15

मैं मानचित्रण प्रकारों के बारे में सर्वोत्तम प्रथाओं के बारे में और C # में विस्तार विधियों का उपयोग करके कुछ प्रश्न पूछना चाहता हूं। मुझे पता है कि इस विषय पर पिछले कुछ वर्षों में कई बार चर्चा हुई है, लेकिन मैंने बहुत सारी पोस्ट पढ़ी हैं और अभी भी संदेह है।

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

मेरे पास 4 संभावित समाधान हैं:

  1. .ToPerson () उपभोक्ता वर्ग में विधि। यह सरल है, लेकिन यह मेरे लिए सिंगल रिस्पॉन्सिबिलिटी पैटर्न को तोड़ने जैसा लगता है, विशेष रूप से यह कि उपभोक्ता वर्ग को अन्य वर्गों (कुछ अन्य बाहरी एपीआई द्वारा आवश्यक) के लिए मैप किया जाता है, इसलिए इसे कई मैपिंग विधियों को शामिल करना होगा।

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

  3. विस्तारक विधियों के साथ कन्वर्टर्स वर्ग। इस तरह से मैं लिख सकता हूँ। उपभोक्ता वर्ग के लिए .PPerson () विधि और जब एक और API को उसके स्वयं के NewConsumer वर्ग के साथ पेश किया जाता है, तो मैं बस एक और विस्तार विधि लिख सकता हूँ और इसे एक ही फ़ाइल में रख सकता हूँ। मैंने एक राय सुनी है कि विस्तार के तरीके सामान्य रूप से बुरे हैं और इसका उपयोग केवल तभी किया जाना चाहिए जब बिल्कुल आवश्यक हो ताकि मुझे वापस पकड़ा जा सके। अन्यथा मुझे यह समाधान पसंद है

  4. कनवर्टर / मैपर वर्ग। मैं एक अलग वर्ग बनाता हूं जो रूपांतरणों को संभालेगा और उन तरीकों को लागू करेगा जो स्रोत वर्ग के उदाहरण को तर्क के रूप में लेंगे और गंतव्य वर्ग के उदाहरण को वापस करेंगे।

संक्षेप में, मेरी समस्या को प्रश्नों की संख्या तक कम किया जा सकता है (जो मैंने ऊपर वर्णित है, उसके संदर्भ में):

  1. क्या रूपांतरण विधि (POCO?) ऑब्जेक्ट को अंदर रखा जा रहा है (जैसे .Toerson () विधि को उपभोक्ता वर्ग में) एकल जिम्मेदारी पैटर्न को तोड़ने के रूप में माना जाता है?

  2. एकल जिम्मेदारी पैटर्न को तोड़ने पर विचार करने वाले (डीटीओ-जैसे) वर्ग में कंस्ट्रक्टिंग कंस्ट्रक्टर का उपयोग किया जाता है? विशेष रूप से अगर ऐसे वर्ग को कई स्रोत प्रकारों से परिवर्तित किया जा सकता है, तो एकाधिक परिवर्तित निर्माणकर्ताओं की आवश्यकता होगी?

  3. क्या मूल विधि स्रोत कोड तक पहुँच प्राप्त करते समय विस्तार विधियों का उपयोग करना बुरा व्यवहार माना जाता है? क्या इस तरह के व्यवहार को तर्क को अलग करने के लिए व्यवहार्य पैटर्न के रूप में इस्तेमाल किया जा सकता है या यह एक विरोधी पैटर्न है?


क्या Personक्लास एक डीटीओ है? क्या इसमें कोई व्यवहार है?
याकूब मस्साद

जहाँ तक मुझे पता है कि यह तकनीकी रूप से एक डीटीओ है। इसमें विस्तार विधियों के रूप में कुछ तर्क "इंजेक्ट" होते हैं, लेकिन यह तर्क "ConvertToThatClass" प्रकार के तरीकों तक सीमित है। ये सभी विरासत विधियां हैं। मेरा काम नई कार्यक्षमता को लागू करना है जो इन वर्गों में से कुछ का उपयोग करता है, लेकिन मुझे बताया गया था कि मुझे वर्तमान दृष्टिकोण से चिपके नहीं रहना चाहिए, अगर यह केवल सुसंगत रखने के लिए बुरा है। तो मैं सोच रहा था कि अगर विस्तार के तरीकों का उपयोग करने के साथ यह मौजूदा दृष्टिकोण एक अच्छा है और अगर मुझे इसके साथ रहना चाहिए, या कुछ और प्रयास करना चाहिए
emsi

जवाबों:


11

क्या रूपांतरण विधि (POCO?) ऑब्जेक्ट को अंदर रखा जा रहा है (जैसे .Toerson () विधि को उपभोक्ता वर्ग में) एकल जिम्मेदारी पैटर्न को तोड़ने के रूप में माना जाता है?

हां, क्योंकि रूपांतरण एक और जिम्मेदारी है।

एकल जिम्मेदारी पैटर्न को तोड़ने पर विचार करने वाले (डीटीओ-जैसे) वर्ग में कंस्ट्रक्टिंग कंस्ट्रक्टर का उपयोग किया जाता है? विशेष रूप से अगर ऐसे वर्ग को कई स्रोत प्रकारों से परिवर्तित किया जा सकता है, तो एकाधिक परिवर्तित निर्माणकर्ताओं की आवश्यकता होगी?

हां, रूपांतरण एक और जिम्मेदारी है। यह कोई फर्क नहीं पड़ता अगर आप इसे कंस्ट्रक्टरों के माध्यम से या रूपांतरण विधियों (जैसे ToPerson) के माध्यम से करते हैं ।

क्या मूल विधि स्रोत कोड तक पहुँच प्राप्त करते समय विस्तार विधियों का उपयोग करना बुरा व्यवहार माना जाता है?

जरुरी नहीं। आप विस्तार विधियाँ बना सकते हैं भले ही आपके पास उस वर्ग का स्रोत कोड हो जिसे आप विस्तारित करना चाहते हैं। मुझे लगता है कि आप एक विस्तार विधि बनाते हैं या नहीं, ऐसी विधि की प्रकृति द्वारा निर्धारित किया जाना चाहिए। उदाहरण के लिए, क्या इसमें बहुत सारे तर्क हैं? क्या यह किसी और चीज पर निर्भर करता है जो वस्तु के सदस्य ही हैं? मैं कहूंगा कि आपके पास एक्सटेंशन पद्धति नहीं होनी चाहिए, जिसमें काम करने के लिए निर्भरता की आवश्यकता हो या जिसमें जटिल तर्क हो। विस्तार विधि में केवल तर्क का सबसे सरल होना चाहिए।

क्या इस तरह के व्यवहार को तर्क को अलग करने के लिए व्यवहार्य पैटर्न के रूप में इस्तेमाल किया जा सकता है या यह एक विरोधी पैटर्न है?

यदि तर्क जटिल है, तो मुझे लगता है कि आपको एक्सटेंशन विधि का उपयोग नहीं करना चाहिए। जैसा कि मैंने पहले उल्लेख किया है, आपको केवल सरलतम चीजों के लिए विस्तार विधियों का उपयोग करना चाहिए। मैं रूपांतरण को सरल नहीं मानूंगा।

मेरा सुझाव है कि आप रूपांतरण सेवाएँ बनाएँ। आपके पास इसके लिए एक एकल सामान्य इंटरफ़ेस हो सकता है:

public interface IConverter<TSource,TDestination>
{
    TDestination Convert(TSource source_object);
}

और आप इस तरह कन्वर्टर्स हो सकते हैं:

public class PersonToCustomerConverter : IConverter<Person,Customer>
{
    public Customer Convert(Person source_object)
    {
        //Do the conversion here. Note that you can have dependencies injected to this class
    }
}

और आप किसी भी वर्ग के लिए एक कनवर्टर (जैसे ) को इंजेक्ट करने के लिए डिपेंडेंसी इंजेक्शन का उपयोग कर सकते हैं IConverter<Person,Customer>जिसके बीच Personऔर बदलने की क्षमता की आवश्यकता होती है Customer


अगर मैं गलत नहीं हूं, तो IConverterपहले से ही ढांचा है, बस लागू होने का इंतजार है।
रबरडैक

@ रबरडक, यह कहाँ मौजूद है?
याकूब मासाद

मैं सोच रहा था IConvertable, जो हम यहां नहीं देख रहे हैं। मेरी गलती।
रबरडैक

आह हा! मैंने वही पाया जो मैं @YacoubMassad के बारे में सोच रहा था। कॉल करते समय Converterउपयोग किया जाता है । msdn.microsoft.com/en-us/library/kt456a2y(v=vs.110).aspx मुझे नहीं पता कि यह ओपी के लिए कितना उपयोगी है। ListConvertAll
रबरडैक

प्रासंगिक भी। किसी और ने दृष्टिकोण लिया है जिसे आप यहां प्रस्तावित करते हैं। codereview.stackexchange.com/q/51889/41243
RubberDuck

5

क्या रूपांतरण विधि (POCO?) ऑब्जेक्ट (जैसे .ToPerson()उपभोक्ता वर्ग में विधि ) को अंदर लाना एकल जिम्मेदारी पैटर्न को तोड़ना माना जाता है?

हाँ। एक Consumerवर्ग एक उपभोक्ता से संबंधित डेटा (और संभवतः कुछ कार्यों को करने) के लिए ज़िम्मेदार है और खुद को दूसरे, असंबंधित प्रकार में परिवर्तित करने के लिए ज़िम्मेदार नहीं होना चाहिए ।

एकल जिम्मेदारी पैटर्न को तोड़ने पर विचार करने वाले (डीटीओ-जैसे) वर्ग में कंस्ट्रक्टिंग कंस्ट्रक्टर का उपयोग किया जाता है? विशेष रूप से अगर ऐसे वर्ग को कई स्रोत प्रकारों से परिवर्तित किया जा सकता है, तो एकाधिक परिवर्तित निर्माणकर्ताओं की आवश्यकता होगी?

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

क्या मूल विधि स्रोत कोड तक पहुँच प्राप्त करते समय विस्तार विधियों का उपयोग करना बुरा व्यवहार माना जाता है?

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

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


2

कैसे AutoMapper या कस्टम मैपर का उपयोग करने के बारे में पसंद किया जा सकता है

MyMapper
   .CreateMap<Person>()
   .To<PersonViewModel>()
   .Map(p => p.Name, vm => vm.FirstName)
   .To<SomeDTO>()
   .Map(...);

डोमेन से

 db.Persons
   .ToListAsync()
   .Map<PersonViewModel>();

हुड के तहत आप ऑटोमैपर को सार कर सकते हैं या अपने स्वयं के मैपर को रोल कर सकते हैं


2

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

public interface IConverter<TSource, TDestination>
{
    TDestination Convert(TSource source_object);
    TSource Convert(TDestination source_object);
}

public class PersonCustomerConverter : IConverter<Person, Customer>
{
    public Customer Convert(Person source_object)
    {
        //Do the conversion here. Note that you can have dependencies injected to this class
    }
    public Person Convert(Customer source_object)
    {
        //Do the conversion here. Note that you can have dependencies injected to this class
    }
}

मुझे लगता है कि AutoMapper वास्तव में सरल मैपिंग ऑपरेशन करने के लिए थोड़ा बहुत है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.