कीवर्ड का उपयोग करते समय DRY सिद्धांत को कैसे लागू किया जाए?


23

इन तरीकों पर विचार करें:

public List<Employee> GetAllEmployees()
{
    using (Entities entities = new Entities())
    {
        return entities.Employees.ToList();
    }
}

public List<Job> GetAllJobs()
{
    using (Entities entities = new Entities())
    {
        return entities.Jobs.ToList();
    }
}

public List<Task> GetAllTasksOfTheJob(Job job)
{
    using (Entities entities = new Entities())
    {
        return entities.Tasks.Where(t => t.JobId == job.Id).ToList();
    }
}

ब्लॉक का उपयोग करना समान है और यहां 3 बार दोहराया गया है (बेशक, वास्तविक एप्लिकेशन में 100 से अधिक बार)। usingब्लॉक के लिए DRY (डोंट रिपीट योरसेल्फ) प्रिंसिपल को लागू करना कैसे संभव है ? क्या इसे DRY प्रिंसिपल का उल्लंघन माना जाता है?

अपडेट: मैं इस बारे में बात नहीं कर रहा हूं कि usingब्लॉक के अंदर क्या लागू किया गया है । मैं वास्तव में यहाँ क्या मतलब है, है using (Entities entities = new Entities())। यह पंक्ति 100 बार या उससे अधिक दोहराई जाती है।


2
क्या यह C # है? आपके प्रश्न का उत्तर भाषा पर निर्भर हो सकता है
डेविड

हाँ @ डेविड, क्षमा करें कि मैंने अपनी भाषा का उल्लेख नहीं किया। यह उत्तर को कैसे प्रभावित कर सकता है?
सईद नेमाटी

कुछ भाषाओं में विशेष रूप से वाक्यविन्यास हो सकते हैं जो आपके कोड के एक छोटे से कारक की सहायता कर सकते हैं। मैं C # नहीं जानता, लेकिन रूबी में, मुझे लगता है कि आप ब्लॉक का उपयोग कर सकते हैं।
डेविड

कथन का उपयोग वास्तव में मदद से बचने के दोहराव कोडिंग के साथ संसाधन निपटान का प्रबंधन करते हुए शुष्क सिद्धांत लागू करने के लिए सी # भाषा समर्थन प्रदान करता निपटान डिजाइन पैटर्न । इसका मतलब यह नहीं है कि हम चीजों को बनाने के तरीके नहीं खोज सकते हैं! व्यक्तिगत रूप से, मैं DRY को एक पुनरावर्ती प्रक्रिया के रूप में समझता हूं।
जॉन टोबलर

जवाबों:


24

एक विचार इसे एक फ़ंक्शन के साथ लपेटने का होगा जो एक लेता है Func

कुछ इस तरह

public K UsingT<T,K>(Func<T,K> f) where T:IDisposable,new()
{
    using (T t = new T())
    {
        return f(t);
    }
}

फिर आपका उपरोक्त कोड बन जाता है

public List<Employee> GetAllEmployees()
{
    return UsingT<Entities,List<Employee>>(e=>e.Employees.ToList());
}

public List<Job> GetAllJobs()
{
    return UsingT<Entities,List<Job>>(e=>e.Jobs.ToList());
}

public List<Task> GetAllTasksOfTheJob(Job job)
{
    return UsingT<Entities,List<Task>>(e=>e.Tasks.Where(t => t.JobId == job.Id).ToList());
}

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

ईमानदार होने के लिए हालांकि इस तरह का कोड पठनीयता को बिल्कुल भी मदद नहीं करता है। मेरे अनुभव में अधिक जूनियर सहकर्मियों के साथ-साथ वास्तव में कठिन समय है।

अपडेट आपके द्वारा विचार किए जा सकने वाले सहायकों पर कुछ अतिरिक्त विविधताएँ

//forget the Entities type param
public T UsingEntities<T>(Func<Entities,T> f)
{
    using (Entities e = new Entities())
    {
        return f(e);
    }
}
//forget the Entities type param, and return an IList
public IList<T> ListFromEntities<T>(Func<Entities,IEnumerable<T>> f)
{
    using (Entities e = new Entities())
    {
        return f(e).ToList();
    }
}
//doing the .ToList() forces the results to enumerate before `e` gets disposed.

1
+1 अच्छा समाधान, हालांकि यह वास्तविक समस्या (मूल प्रश्न में शामिल नहीं) को संबोधित नहीं करता है, अर्थात की कई घटनाएं Entities
back2dos

@ डॉक ब्राउन: मुझे लगता है कि मैंने आपको फ़ंक्शन नाम से हराया। मैंने IEnumerableसमारोह के बाहर छोड़ दिया मामले IEnumerableमें टी के कोई भी गैर- गुण थे जो कॉलर वापस चाहता था, लेकिन आप सही हैं, यह इसे थोड़ा साफ करेगा। शायद सिंगल और IEnumerableपरिणाम दोनों के लिए एक सहायक होना अच्छा होगा। उस ने कहा, मुझे अभी भी लगता है कि यह इस बात की मान्यता को धीमा करता है कि कोड क्या करता है, खासकर किसी ऐसे व्यक्ति के लिए जो बहुत सारे जेनरिक और लैम्ब्डा का उपयोग करने का आदी नहीं है (जैसे आपके सहकर्मी जो एसओ पर नहीं हैं :))
Brook

+1 मुझे लगता है कि यह दृष्टिकोण ठीक है। और पठनीयता में सुधार किया जा सकता है। उदाहरण के लिए, "ToList" डालें WithEntities, Func<T,IEnumerable<K>>इसके बजाय का उपयोग करें Func<T,K>, और "WithEntities" को एक बेहतर नाम (जैसे SelectEntities) दें। और मुझे नहीं लगता कि "संस्थाओं" को यहां एक सामान्य पैरामीटर होना चाहिए।
डॉक्टर ब्राउन

1
यह काम करने के लिए, की कमी करने की आवश्यकता होगी where T : IDisposable, new(), के रूप में usingकी आवश्यकता है IDisposableकाम करने के लिए।
एंथनी Pegram

1
@ एंथनी पेग्राम: फिक्स्ड, धन्यवाद! (यह है कि मुझे एक ब्राउज़र विंडो में C # कोडिंग के लिए क्या मिलता है)
Brook

23

मेरे लिए यह एक ही संग्रह में कई बार foreach-ing के बारे में चिंता करने जैसा होगा: यह सिर्फ कुछ है जो आपको करने की आवश्यकता है। आगे इसे अमूर्त करने का कोई भी प्रयास कोड को बहुत कम पठनीय बनाता है।


महान उपमा @ बान। +1
सईद नेमाटी

3
मैं सहमत नहीं हूँ, क्षमा करें। लेन-देन के दायरे के बारे में ओपी की टिप्पणियों को पढ़ें और सोचें कि आपको क्या करना है जब आपने 500 बार उस तरह के कोड को लिखा है, और फिर ध्यान दें कि आपको 500 बार एक ही बात को बदलना होगा। इस तरह का कोड दोहराना तभी ठीक हो सकता है जब आपके पास उन कार्यों का <10 हो।
Doc Brown

ओह, और मत भूलना, यदि आप प्रत्येक के लिए समान संग्रह को 10 बार से अधिक समान तरीके से करते हैं, तो आपके प्रत्येक के लिए समान-दिखने वाले कोड के साथ, आपको कुछ सामान्यीकरण के बारे में निश्चित रूप से सोचना चाहिए।
Doc Brown

1
मेरे लिए यह सिर्फ ऐसा लगता है कि आप एक ऑन्लाइनर में तीन लाइनर बना रहे हैं ... आप अभी भी खुद को दोहरा रहे हैं।
जिम वोल्फ

यह संदर्भ पर निर्भर है, अगर foreachएक बहुत बड़े संग्रह पर है या foreachलूप के भीतर तर्क उदाहरण के लिए समय लेने वाला है। एक आदर्श वाक्य जिसे मैं अपनाने आया हूं: जुनूनी मत बनो लेकिन हमेशा अपने दृष्टिकोण के प्रति सचेत
रहो

9

ऐसा लगता है कि आप DRY सिद्धांत के साथ "वंस एंड ओनली वंस" सिद्धांत को भ्रमित कर रहे हैं। DRY सिद्धांत बताता है:

ज्ञान के हर टुकड़े में एक प्रणाली के भीतर एक एकल, अस्पष्ट, आधिकारिक प्रतिनिधित्व होना चाहिए।

हालांकि एक बार और केवल एक बार सिद्धांत थोड़ा अलग है।

[डीआरवाई] सिद्धांत एक बार वंडरऑनली के समान है, लेकिन एक अलग उद्देश्य के साथ। एक बारAndOnlyOnce के साथ, आपको दोहराए गए कोड और कार्यक्षमता को खत्म करने के लिए रिफ्लेक्टर के लिए प्रोत्साहित किया जाता है। DRY के साथ, आप अपने सिस्टम में उपयोग किए जाने वाले ज्ञान के हर टुकड़े के एकल, निश्चित स्रोत की पहचान करने की कोशिश करते हैं, और फिर उस ज्ञान (कोड, प्रलेखन, परीक्षण, आदि) के लागू उदाहरण उत्पन्न करने के लिए उस स्रोत का उपयोग करते हैं।

DRY सिद्धांत का उपयोग आमतौर पर वास्तविक तर्क के संदर्भ में किया जाता है, न कि बयानों का उपयोग करके अतिरेक:

एक कार्यक्रम की संरचना को ध्यान में रखते हुए DRY कठिन है, और कम मूल्य। यह व्यावसायिक नियम हैं, यदि कथन, गणित के सूत्र और मेटाडेटा केवल एक ही स्थान पर दिखाई देने चाहिए। WET सामान - HTML पृष्ठ, निरर्थक परीक्षण डेटा, अल्पविराम और {} delimiters - सभी को अनदेखा करना आसान है, इसलिए उन्हें कम करना महत्वपूर्ण है।

स्रोत


7

मैं usingयहाँ का उपयोग देखने में विफल रहा :

कैसा रहेगा:

public List<Employee> GetAllEmployees() {
    return (new Entities()).Employees.ToList();
}
public List<Job> GetAllJobs() {
    return (new Entities()).Jobs.ToList();
}
public List<Task> GetAllTasksOfTheJob(Job job) {
    return (new Entities()).Tasks.Where(t => t.JobId == job.Id).ToList();
}

या इससे भी बेहतर, क्योंकि मुझे नहीं लगता कि आपको हर बार एक नई वस्तु बनाने की आवश्यकता है।

private Entities entities = new Entities();//not sure C# allows for that kind of initialization, but you can do it in the constructor if needed

public List<Employee> GetAllEmployees() {
    return entities.Employees.ToList();
}
public List<Job> GetAllJobs() {
    return entities.Jobs.ToList();
}
public List<Task> GetAllTasksOfTheJob(Job job) {
    return entities.Tasks.Where(t => t.JobId == job.Id).ToList();
}

DRY को भंग करने के लिए: DRY इस स्तर पर लागू नहीं होता है। वास्तव में कोई भी सिद्धांत वास्तव में ऐसा नहीं करता, सिवाय पठनीयता के। उस स्तर पर DRY को लागू करने की कोशिश करना वास्तव में सिर्फ आर्किटेक्चरल माइक्रो-ऑप्टिमाइज़ेशन है, जो कि सभी माइक्रो-ऑप्टिमाइज़ेशन की तरह ही बाइक-शेडिंग है और इससे कोई समस्या हल नहीं होती है, लेकिन नए लोगों को पेश करने का जोखिम भी है।
अपने स्वयं के अनुभव से, मुझे पता है कि यदि आप उस स्तर पर कोड अतिरेक को कम करने का प्रयास करते हैं, तो आप कोड गुणवत्ता पर नकारात्मक प्रभाव डालते हैं, जो वास्तव में स्पष्ट और सरल था, उस पर आपत्ति करके।

संपादित करें:
ठीक है। तो समस्या वास्तव में उपयोग करने वाला कथन नहीं है, समस्या उस वस्तु पर निर्भरता है जिसे आप हर बार बनाते हैं। मैं एक निर्माता को इंजेक्शन लगाने का सुझाव दूंगा:

private delegate Entities query();//this should be injected from the outside (upon construction for example)
public List<Employee> GetAllEmployees() {
    using (var entities = query()) {//AFAIK C# can infer the type here
        return entities.Employees.ToList();
    }
}
//... and so on

2
लेकिन @ back2dos, ऐसे कई स्थान हैं जहाँ हम using (CustomTransaction transaction = new CustomTransaction())अपने कोड में कोड ब्लॉक का उपयोग करते हैं ताकि एए लेनदेन के दायरे को परिभाषित किया जा सके। इसे एक ही वस्तु में नहीं बांधा जा सकता है और प्रत्येक स्थान पर आप लेन-देन का उपयोग करना चाहते हैं, आपको एक ब्लॉक लिखना चाहिए। अब क्या आप से है कि लेनदेन के प्रकार को बदलना चाहते हैं CustomTransactionकरने के लिए BuiltInTransaction500 से अधिक तरीकों में? यह मुझे एक दोहराए जाने वाला काम लगता है और DRY प्रिंसिपल का एक ब्रीच उदाहरण है।
सईद नेमाटी

3
यहां "उपयोग" करने से डेटा संदर्भ बंद हो जाता है, इसलिए इन विधियों के बाहर आलसी लोडिंग संभव नहीं है।
स्टीवन स्ट्रिगा

@ सईद: जब आप निर्भरता इंजेक्शन में देखते हैं। लेकिन ऐसा लगता है कि सवाल में कहा गया मामला काफी अलग है।
बजे एक CVn

@ सईद: पोस्ट अपडेट की गई।
back2dos

@WeekendWarrior है using (इस संदर्भ में) अभी भी एक अधिक अज्ञात "सुविधाजनक वाक्यविन्यास" है? यह =) का उपयोग करने के लिए इतना अच्छा क्यों है
Coops

4

न केवल डुप्लिकेट कोड का उपयोग कर रहा है (वैसे यह डुप्लिकेट कोड है और वास्तव में एक try..catch..finally स्टेटमेंट की तुलना करता है) लेकिन टॉलिस्ट भी। मैं आपके कोड को इस तरह रिफ्लेक्टर करूंगा:

 public List<T> GetAll(Func<Entities, IEnumerable<T>> getter) {
    using (Entities entities = new Entities())
    {
        return getter().ToList();
    }
 }

public List<Employee> GetAllEmployees()
{
    return GetAll(e => e.Employees);
}

public List<Job> GetAllJobs()
{
    return GetAll(e => e.Jobs);
}

public List<Task> GetAllTasksOfTheJob(Job job)
{
    return GetAll(e => e.Tasks.Where(t => t.JobId == job.Id));
}

3

चूंकि पिछले एक को छोड़कर यहां किसी भी प्रकार का कोई व्यावसायिक तर्क नहीं है। मेरे विचार में इसकी वास्तव में DRY नहीं है।

पिछले एक फ्लॉप का उपयोग करने वाले ब्लॉक में कोई DRY नहीं है, लेकिन मुझे लगता है कि क्लॉज को जहां कभी भी इस्तेमाल किया जाना चाहिए, वहां बदलना चाहिए।

यह कोड जनरेटर के लिए एक विशिष्ट काम है। कोड जनरेटर को लिखें और कवर करें और इसे प्रत्येक प्रकार के लिए उत्पन्न करें।


न @ वरुणमुर। यहां गलतफहमी थी। DRY से, मेरा मतलब था using (Entities entities = new Entities())ब्लॉक। मेरा मतलब है, कोड की यह लाइन 100 बार दोहराई जाती है और अधिक से अधिक दोहराई जा रही है।
सईद नेमाटी

डीआरवाई मुख्य रूप से इस तथ्य से आता है कि आपको परीक्षण मामलों को लिखने की आवश्यकता है कि कई बार और एक जगह पर एक बग का मतलब है कि आपको इसे 100 स्थानों पर ठीक करना होगा। उपयोग (Entities ...) को तोड़ने के लिए एक कोड भी सरल है। इसे दूसरी कक्षा में विभाजित करने या रखने की आवश्यकता नहीं है। यदि आप अभी भी इसे सरल बनाने पर जोर देते हैं। मैं रूबी से एक मिल्ड कॉल वापस समारोह का सुझाव देंगे।
अरुणमुर

1
@ स्वर्णमुर - हमेशा तोड़ना सरल नहीं है। मेरे पास अक्सर एक अच्छा तर्क होता है जो यह निर्धारित करता है कि डेटा के संदर्भ में किन विकल्पों का उपयोग करना है। यह बहुत संभव है कि गलत कनेक्शन स्ट्रिंग को पारित किया जा सकता है।
मोर्गन हेरलकर

1
@ironcode - उन स्थितियों में यह किसी कार्य को बंद करने के लिए समझ में आता है। लेकिन इन उदाहरणों में तोड़ने के लिए बहुत कठिन है। यहां तक ​​कि अगर यह फिक्स को तोड़ता है, तो अक्सर एंटिटीज़ क्लास की परिभाषा में ही होता है, जिसे एक अलग परीक्षण के मामले में कवर किया जाना चाहिए।
अरुणमुर

+1 @ वरुणमुर - मैं सहमत हूँ। मैं आमतौर पर खुद ऐसा करता हूं। मैं उस फ़ंक्शन के लिए परीक्षण लिखता हूं, लेकिन यह आपके उपयोग कथन के लिए परीक्षण लिखने के लिए शीर्ष पर थोड़ा सा लगता है।
मॉर्गन हेरलॉकर

2

चूँकि आप एक ही डिस्पोजेबल ऑब्जेक्ट को बार-बार बना रहे हैं और नष्ट कर रहे हैं, इसलिए आपकी कक्षा खुद को आईडीसोपयोगी पैटर्न लागू करने के लिए एक अच्छा उम्मीदवार है।

class ThisClass : IDisposable
{
    protected virtual Entities Context { get; set; }

    protected virtual void Dispose( bool disposing )
    {
        if ( disposing && Context != null )
            Context.Dispose();
    }

    public void Dispose()
    {
        Dispose( true );
    }

    public ThisClass()
    {
        Context = new Entities();
    }

    public List<Employee> GetAllEmployees()
    {
        return Context.Employees.ToList();
    }

    public List<Job> GetAllJobs()
    {
        return Context.Jobs.ToList();
    }

    public List<Task> GetAllTasksOfTheJob(Job job)
    {
        return Context.Tasks.Where(t => t.JobId == job.Id).ToList();
    }
}

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

public static List<Employee> GetAllEmployees( Entities entities )
{
    return entities.Employees.ToList();
}

public static List<Job> GetAllJobs( Entities entities )
{
    return entities.Jobs.ToList();
}

public static List<Task> GetAllTasksOfTheJob( Entities entities, Job job )
{
    return entities.Tasks.Where(t => t.JobId == job.Id).ToList();
}

1

असंपीड़ित जादू का मेरा पसंदीदा बिट!

public class Blah
{
  IEnumerable<T> Wrap(Func<Entities, IEnumerable<T>> act)
  {
    using(var entities = new Entities()) { return act(entities); }
  }

  public List<Employee> GetAllEmployees()
  {
    return Wrap(e => e.Employees.ToList());
  }

  public List<Job> GetAllJobs()
  {
    return Wrap(e => e.Jobs.ToList());
  }

  public List<Task> GetAllTasksOfTheJob(Job job)
  {
    return Wrap(e => e.Tasks.Where(x ....).ToList());
  }
}

Wrapकेवल उस सार को या जो भी जादू की आवश्यकता है, मौजूद है। मुझे यकीन नहीं है कि मैं हर समय यह सुझाऊंगा लेकिन इसका उपयोग करना संभव है। "बेहतर" विचार एक DI कंटेनर का उपयोग करने के लिए होगा, जैसे कि StructureMap, और अनुरोध संदर्भ के लिए केवल एंटिटी वर्ग को स्कोप करें, इसे नियंत्रक में इंजेक्ट करें, और फिर इसे अपने नियंत्रक की आवश्यकता के बिना जीवनचक्र का ख्याल रखें।


फंक <IEnumerable <T>, एंटिटीज़> के लिए आपके प्रकार के पैरामाटर्स गलत क्रम में हैं। (मेरा जवाब देखें जो मूल रूप से एक ही बात है)
ब्रुक

ठीक है, सही करने के लिए काफी आसान है। मुझे कभी याद नहीं रहा कि सही आदेश क्या है। मैं Funcपर्याप्त उपयोग करता हूं जो मुझे चाहिए।
ट्रैविस
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.