इंटरफ़ेस डिज़ाइन जहां फ़ंक्शन को एक विशिष्ट अनुक्रम में बुलाया जाना चाहिए


24

कार्य कुछ इनपुट विनिर्देश के अनुसार, डिवाइस के भीतर हार्डवेयर के एक टुकड़े को कॉन्फ़िगर करना है। इसे निम्नानुसार प्राप्त किया जाना चाहिए:

1) कॉन्फ़िगरेशन जानकारी एकत्र करें। यह अलग-अलग समय और स्थानों पर हो सकता है। उदाहरण के लिए, मॉड्यूल ए और मॉड्यूल बी दोनों (अलग-अलग समय पर) मेरे मॉड्यूल से कुछ संसाधनों का अनुरोध कर सकते हैं। वे 'संसाधन' वास्तव में विन्यास क्या हैं।

2) यह स्पष्ट होने के बाद कि कोई और अनुरोध साकार नहीं होने वाला है, एक स्टार्टअप कमांड, अनुरोधित संसाधनों का सारांश देते हुए, हार्डवेयर में भेजने की आवश्यकता है।

3) उसके बाद ही, उक्त संसाधनों का विस्तृत विन्यास (और अवश्य) किया जा सकता है।

4) इसके अलावा, केवल 2 के बाद), घोषित कॉलर्स के लिए चयनित संसाधनों का राउटिंग (और अवश्य) किया जाना चाहिए।


कीड़े के लिए एक सामान्य कारण, यहां तक ​​कि मेरे लिए, जिसने बात लिखी थी, इस आदेश को गलत कर रहा है। पहली बार कोड देखने वाले किसी व्यक्ति द्वारा इंटरफ़ेस का उपयोग करने के लिए क्या नामकरण परंपराएँ, डिज़ाइन या मैकेनिज़्म नियोजित कर सकते हैं?


स्टेज 1 बेहतर कहा जाता है discoveryया handshake?
rwong

1
टेम्पोरल कपलिंग एक एंटी-पैटर्न है और इससे बचा जाना चाहिए।

1
प्रश्न का शीर्षक मुझे लगता है कि आपको स्टेप बिल्डर पैटर्न में रुचि हो सकती है ।
जोशुआ टेलर

जवाबों:


45

यह एक नया स्वरूप है, लेकिन आप कई API का दुरुपयोग रोक सकते हैं, लेकिन ऐसा कोई भी तरीका उपलब्ध नहीं है जिसे कॉल नहीं किया जाना चाहिए।

उदाहरण के लिए, के बजाय first you init, then you start, then you stop

आपका कंस्ट्रक्टर initएक ऑब्जेक्ट है जिसे शुरू किया जा सकता है और startएक सत्र बनाता है जिसे रोका जा सकता है।

बेशक, यदि आपके पास एक समय में एक सत्र के लिए प्रतिबंध है, तो आपको उस मामले को संभालने की आवश्यकता है जहां कोई व्यक्ति पहले से ही सक्रिय के साथ एक बनाने की कोशिश करता है।

अब उस तकनीक को अपने मामले में लागू करें।


zlibऔर jpeglibदो उदाहरण हैं जो आरंभीकरण के लिए इस पैटर्न का पालन करते हैं। फिर भी, डेवलपर्स को अवधारणा सिखाने के लिए बहुत सारे दस्तावेज़ आवश्यक हैं।
रवांग

5
यह बिल्कुल सही उत्तर है: यदि ऑर्डर मायने रखता है, तो प्रत्येक फ़ंक्शन एक परिणाम देता है जिसे फिर अगले चरण को करने के लिए बुलाया जा सकता है। कंपाइलर ही डिजाइन की बाधाओं को लागू करने में सक्षम है।

2
यह कदम बिल्डर पैटर्न के समान है ; केवल उस इंटरफ़ेस को प्रस्तुत करें जो किसी दिए गए चरण में समझ में आता है।
जोशुआ टेलर

@JoshuaTaylor मेरा उत्तर एक कदम बिल्डर पैटर्न कार्यान्वयन है :)
सिल्वियू बर्किया

@SilviuBurcea आपका उत्तर एक कदम बिल्डर कार्यान्वयन अनुपात नहीं है, लेकिन मैं यहाँ के बजाय इस पर टिप्पणी करूँगा।
जोशुआ टेलर

19

आपके पास स्टार्टअप विधि एक ऐसी वस्तु लौटा सकती है जो कॉन्फ़िगरेशन के लिए आवश्यक पैरामीटर है:

संसाधन * MyModule :: GetResource ();
MySession * MyModule :: स्टार्टअप ();
शून्य संसाधन :: कॉन्फ़िगर करें (MySession * सत्र);

यहां तक ​​कि अगर आपका MySessionसिर्फ एक खाली ढांचा है, तो यह प्रकार की सुरक्षा के माध्यम से लागू होगा Configure()जिसे स्टार्टअप से पहले कोई विधि नहीं कहा जा सकता है।


किसी को किस करने से रोकता है module->GetResource()->Configure(nullptr)?
svick

@ शविक: कुछ भी नहीं, लेकिन आपको स्पष्ट रूप से ऐसा करना चाहिए। यह दृष्टिकोण आपको बताता है कि यह क्या अपेक्षा करता है और उस उम्मीद को दरकिनार करना एक सचेत निर्णय है। अधिकांश प्रोग्रामिंग भाषाओं के साथ, कोई भी आपको अपने आप को पैर में गोली मारने से नहीं रोकता है। लेकिन यह एक एपीआई द्वारा हमेशा अच्छा होता है यह स्पष्ट रूप से इंगित करने के लिए कि आप ऐसा कर रहे हैं;)
माइकल क्लेमेंट

+1 बहुत अच्छा और सरल लगता है। हालाँकि, मैं एक समस्या देख सकता हूँ। यदि मेरे पास वस्तुएं हैं a, b, c, d, तो मैं शुरू कर सकता हूं a, और इसका उपयोग करके पहले से ही शुरू की गई वस्तु के रूप में MySessionउपयोग करने का प्रयास कर सकता हूं b, जबकि वास्तव में यह नहीं है।
वोरैक

8

कैशबुक के उत्तर पर निर्माण - आपको कॉलर को एक नई वस्तु क्यों पेश करनी है, जब आप सिर्फ एक नया इंटरफ़ेस पेश कर सकते हैं? रिब्रांड-पैटर्न:

class IStartable     { public: virtual IRunnable      start()     = 0; };
class IRunnable      { public: virtual ITerminateable run()       = 0; };
class ITerminateable { public: virtual void           terminate() = 0; };

यदि आप एक सत्र को कई बार चला सकते हैं, तो आप ITermineable को IRunnable को लागू करने दे सकते हैं।

आपकी वस्तु:

class Service : IStartable, IRunnable, ITerminateable
{
  public:
    IRunnable      start()     { ...; return this; }
    ITerminateable run()       { ...; return this; }
    void           terminate() { ...; }
}

// And use it like this:
IStartable myService = Service();

// Now you can only call start() via the interface
IRunnable configuredService = myService.start();

// Now you can also call run(), because it is wrapped in the new interface...

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


1
कई के बजाय सिर्फ एक अंतर्निहित वर्ग होने का क्या फायदा है? जैसा कि मैंने प्रस्तावित समाधान के साथ केवल यही अंतर है, मुझे इस विशेष बिंदु में दिलचस्पी होगी।
माइकल ले बारबियर ग्रुएनवाल्ड

1
@ MichaelGrünewald एक वर्ग के साथ सभी इंटरफेस को लागू करना आवश्यक नहीं है, लेकिन कॉन्फ़िगरेशन-प्रकार की वस्तु के लिए, यह इंटरफेस के उदाहरणों के बीच डेटा साझा करने के लिए सबसे सरल कार्यान्वयन तकनीक हो सकती है (यानी, क्योंकि यह समान होने के कारण साझा किया गया है वस्तु)।
जोशुआ टेलर


@JoshuaTaylor इंटरफ़ेस के उदाहरणों के बीच डेटा साझा करना दुगुना है: जबकि इसे लागू करना आसान हो सकता है, हमें "अपरिभाषित स्थिति" (जैसे नॉन-कनेक्टेड सर्वर के क्लाइंट एड्रेस को एक्सेस करना) पर ध्यान नहीं देना होगा। जैसा कि ओपी ने इंटरफ़ेस प्रयोज्य पर जोर दिया, हम दो दृष्टिकोणों को समान रूप से आंक सकते हैं। “स्टेप बिल्डर पैटर्न” बीटीडब्लू को उद्धृत करने के लिए थैंक यू।
माइकल ले बारबियर ग्रुएनवाल्ड

1
@ MichaelGrünewald यदि आप केवल उस विशेष इंटरफेस के माध्यम से ऑब्जेक्ट के साथ बातचीत करते हैं जो किसी दिए गए बिंदु पर निर्दिष्ट है, तो उस स्थिति तक पहुंचने के लिए कोई रास्ता नहीं होना चाहिए (कास्टिंग के बिना, आदि)।
जोशुआ टेलर

2

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

  1. विभिन्न राज्यों अपने डिवाइस में किया जा सकता है की पहचान करें, के रूप में Uninitialised, Started,Configured और इतने पर। सूची को परिमित करना होगा

  2. प्रत्येक राज्य के लिए, structउस राज्य से संबंधित आवश्यक अतिरिक्त जानकारी, जैसे DeviceUninitialised, DeviceStartedऔर इसी तरह एक होल्डिंग को परिभाषित करें।

  3. सभी उपचारों को एक वस्तु में पैक करें DeviceStrategyजहाँ तरीके 2 में परिभाषित संरचनाओं का उपयोग करते हैं। इनपुट और आउटपुट के रूप में। इस प्रकार, आपके पास एक DeviceStarted DeviceStrategy::start (DeviceUninitalised dev)विधि हो सकती है (या जो भी आपके प्रोजेक्ट सम्मेलनों के अनुसार बराबर हो सकती है)।

इस दृष्टिकोण के साथ, एक वैध कार्यक्रम को विधि प्रोटोटाइप द्वारा लागू अनुक्रम में कुछ तरीकों को कॉल करना होगा।

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

जबकि मैंने 3 में वर्णित किया है। एक अद्वितीय DeviceStrategyवर्ग, ऐसी परिस्थितियाँ हैं जहाँ आप कई वर्गों में प्रदान की जाने वाली कार्यक्षमता को विभाजित करना चाह सकते हैं।

उन्हें संक्षेप में, मेरे द्वारा बताए गए डिज़ाइन के मुख्य बिंदु हैं:

  1. प्रतिस्थापन सिद्धांत के कारण, उपकरण राज्यों का प्रतिनिधित्व करने वाले ऑब्जेक्ट अलग-अलग होने चाहिए और विशेष विरासत संबंध नहीं होने चाहिए।

  2. डिवाइस को स्वयं का प्रतिनिधित्व करने वाली वस्तुओं के बजाय शुरुआती वस्तुओं में डिवाइस उपचार पैक करें, ताकि प्रत्येक डिवाइस या डिवाइस राज्य केवल खुद को देखता है, और रणनीति उन सभी को देखती है और उनके बीच संभावित संक्रमण को व्यक्त करती है।

मैं कसम खाता हूँ कि मैंने एक बार इन लाइनों के बाद एक टेलनेट क्लाइंट कार्यान्वयन का विवरण देखा, लेकिन मैं इसे फिर से नहीं पा रहा था। यह एक बहुत ही उपयोगी संदर्भ होता!

,: इसके लिए, या तो अपने अंतर्ज्ञान का पालन करें या संबंध के लिए अपने वास्तविक कार्यान्वयन में विधियों के समतुल्य वर्गों का पता लगाएं “method₂ ~ method, iff। यह एक ही वस्तु पर उनका उपयोग करने के लिए वैध है ”- यह मानते हुए कि आपके डिवाइस पर सभी उपचारों को घेरने वाली एक बड़ी वस्तु है। लिस्टिंग राज्यों के दोनों तरीके शानदार परिणाम देते हैं।


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

2

एक बिल्डर-पैटर्न का उपयोग करें।

एक ऑब्जेक्ट है जिसमें आपके ऊपर उल्लिखित सभी ऑपरेशन के तरीके हैं। हालाँकि, यह इन कार्यों को तुरंत नहीं करता है। यह सिर्फ बाद के लिए प्रत्येक ऑपरेशन को याद करता है। क्योंकि कार्रवाई अभी निष्पादित नहीं की गई है, इसलिए आप उन्हें बिल्डर के पास भेजने के लिए कोई फर्क नहीं पड़ता।

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


1

आपको बस सही ढंग से दस्तावेज़ करने की आवश्यकता है कि इंटरफ़ेस का उपयोग कैसे किया जाता है, और एक ट्यूटोरियल उदाहरण दें।

आपके पास एक डीबगिंग लाइब्रेरी संस्करण भी हो सकता है जो कुछ रनटाइम चेक करता है।

शायद परिभाषित करने और सही ढंग से कुछ नामकरण सम्मेलनों का दस्तावेजीकरण (जैसे preconfigure*, startup*, postconfigure*, run*....)

BTW, कई मौजूदा इंटरफेस एक समान पैटर्न (जैसे X11 टूलकिट) का पालन करते हैं।


एंड्रॉइड एप्लिकेशन गतिविधि जीवनचक्र के समान एक राज्य संक्रमण आरेख, जानकारी को व्यक्त करने के लिए आवश्यक हो सकता है।
rwong

1

यह वास्तव में एक सामान्य और कपटी प्रकार की त्रुटि है, क्योंकि कंपाइलर केवल सिंटैक्स शर्तों को लागू कर सकता है, जबकि आपको अपने क्लाइंट प्रोग्राम को "व्याकरणिक रूप से" सही होने की आवश्यकता होती है।

दुर्भाग्य से, इस तरह की त्रुटि के खिलाफ नामकरण परंपराएं लगभग पूरी तरह से अप्रभावी हैं। यदि आप वास्तव में लोगों को असंगतिपूर्ण चीजें नहीं करने के लिए प्रोत्साहित करना चाहते हैं, तो आपको कुछ प्रकार के कमांड ऑब्जेक्ट को पास करना चाहिए जो कि पूर्व शर्त के लिए मूल्यों के साथ आरंभ किया जाना चाहिए, ताकि वे क्रम से बाहर कदम न कर सकें।


आप की तरह कुछ मतलब है यह ?
वोरैक

1
public class Executor {

private Executor() {} // helper class

  public void execute(MyStepsRunnable r) {
    r.step1();
    r.step2();
    r.step3();
  }
}

interface MyStepsRunnable {

  void step1();
  void step2();
  void step3();
}

इस पैटर्न का उपयोग करके आप सुनिश्चित हैं कि कोई भी कार्यान्वयनकर्ता इस सटीक क्रम में निष्पादित करेगा। आप एक कदम और आगे बढ़ सकते हैं और एक निष्पादनकर्ता बना सकते हैं जो कस्टम निष्पादन पथों के साथ निष्पादनकर्ता का निर्माण करेगा।


में एक और टिप्पणी आप इस कार्यान्वयन बिल्डर एक कदम कहा जाता है, लेकिन यह नहीं है। यदि आपके पास MyStepsRunnable का उदाहरण है, तो आप चरण 1 से पहले चरण 3 को कॉल कर सकते हैं। एक कदम बिल्डर कार्यान्वयन ideone.com/UDECgY की तर्ज पर अधिक होगा । विचार केवल यह है कि step1 चलाकर step2 के साथ कुछ प्राप्त करें। इस प्रकार आप सही क्रम में कॉल करने के लिए मजबूर हैं। जैसे, stackoverflow.com/q/17256627/1281433 देखें ।
जोशुआ टेलर

जिस तरह से इसका उपयोग किया जा सकता है, उसे प्रतिबंधित करने के लिए आप इसे संरक्षित विधियों (या यहां तक ​​कि डिफ़ॉल्ट) के साथ एक अमूर्त वर्ग में बदल सकते हैं। आपको निष्पादक का उपयोग करने के लिए मजबूर किया जाएगा, लेकिन मेरे पास है कि वर्तमान कार्यान्वयन के साथ दोष या दो हो सकते हैं।
सिल्विया बुर्सा

यह अभी भी इसे एक कदम बिल्डर नहीं बनाता है। आपके कोड में, ऐसा कुछ भी नहीं है जो उपयोगकर्ता विभिन्न चरणों के बीच कोड चलाने के लिए कर सकता है । विचार सिर्फ कोड अनुक्रम करने के लिए नहीं है (भले ही इसके सार्वजनिक या निजी, या अन्यथा समझाया हो)। जैसा कि आपके कोड से पता चलता है, बस के साथ ऐसा करना काफी आसान है step1(); step2(); step3();। स्टेप बिल्डर का बिंदु एक एपीआई देना है जो कुछ चरणों को उजागर करता है, और उस क्रम को लागू करने के लिए जिसमें वे कहते हैं। यह प्रोग्रामर को चरणों के बीच अन्य कार्य करने से नहीं रोकना चाहिए।
जोशुआ टेलर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.