इंटरफ़ेस पृथक्करण सिद्धांत: क्या करना है अगर इंटरफेस में महत्वपूर्ण ओवरलैप है?


9

से पियर्सन नई अंतर्राष्ट्रीय संस्करण: फुर्तीली सॉफ्टवेयर विकास, सिद्धांतों, पैटर्न, और व्यवहार :

कभी-कभी, ग्राहकों के विभिन्न समूहों द्वारा आह्वान किए गए तरीके ओवरलैप होंगे। यदि ओवरलैप छोटा है, तो समूहों के लिए इंटरफेस अलग रहना चाहिए। सभी अतिव्यापी इंटरफेस में सामान्य कार्यों को घोषित किया जाना चाहिए। सर्वर वर्ग उन प्रत्येक इंटरफेस से सामान्य कार्यों को इनहेरिट करेगा, लेकिन यह उन्हें केवल एक बार लागू करेगा।

अंकल बॉब, उस मामले के बारे में बात करते हैं जब मामूली ओवरलैप होता है।

महत्वपूर्ण ओवरलैप होने पर हमें क्या करना चाहिए?

बोलो हमारे पास है

Class UiInterface1;
Class UiInterface2;
Class UiInterface3;

Class UiIterface : public UiInterface1, public UiInterface2, public UiInterface3{};

यदि महत्वपूर्ण ओवरलैप है UiInterface1और इसके बीच क्या करना चाहिए UiInterface2?


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

मेरे लिए सवाल थोड़ा अस्पष्ट है, कोई भी मामलों के आधार पर कई अलग-अलग समाधानों के साथ जवाब दे सकता है। ओवरलैप क्यों बढ़ गया?
आर्थर हेवेलिसक

जवाबों:


1

ढलाई

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

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

उदाहरण के लिए, इस तरह से क्लाइंट फ़ंक्शन डिज़ाइन करना अजीब लग सकता है:

// Returns the absolute position of an entity as the sum
// of its own position and the position of its ancestors.
// `position` and `parenting` parameters should point to the 
// same object.
Vec2i abs_position(IPosition* position, IParenting* parenting)
{
     const Vec2i xy = position->xy();
     auto parent = parenting->parent();
     if (parent)
     {
         // If the entity has a parent, return the sum of the
         // parent position and the entity's local position.
         return xy + abs_position(dynamic_cast<IPosition*>(parent),
                                  dynamic_cast<IParenting*>(parent));
     }
     return xy;
}

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

मिक्सिंग जिम्मेदारियाँ बनाम कास्टिंग

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

COM- शैली के दृष्टिकोण (केवल QueryInterfaceभाग) का उपयोग करके , हम डाउनकास्टिंग दृष्टिकोण से खेलते हैं लेकिन कास्टिंग को कोडबेस में एक केंद्रीय स्थान पर समेकित करते हैं, और ऐसा कुछ और कर सकते हैं:

// Returns the absolute position of an entity as the sum
// of its own position and the position of its ancestors.
// `obj` should implement `IPosition` and optionally `IParenting`.
Vec2i abs_position(Object* obj)
{
     // `Object::query_interface` returns nullptr if the interface is
     // not provided by the entity. `Object` is an abstract base class
     // inherited by all entities using this interface query system.
     IPosition* position = obj->query_interface<IPosition>();
     assert(position && "obj does not implement IPosition!");
     const Vec2i xy = position->xy();

     IParenting* parenting = obj->query_interface<IParenting>();
     if (parenting && parenting->parent()->query_interface<IPosition>())
     {
         // If the entity implements IParenting and has a parent, 
         // return the sum of the parent position and the entity's 
         // local position.
         return xy + abs_position(parenting->parent());
     }
     return xy;
}

... निश्चित रूप से टाइप-सेफ रैपर और उन सभी के साथ, जो आप कच्चे पॉइंटर्स की तुलना में कुछ सुरक्षित प्राप्त करने के लिए केंद्र बना सकते हैं।

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

टेम्पलेट्स

यदि टेम्प्लेट एक संभावना है (हमारे पास पहले से आवश्यक संकलन-समय की जानकारी है जो उस समय तक नहीं खो जाती है जब तक हम किसी वस्तु को पकड़ नहीं लेते हैं, अर्थात), तो हम बस यह कर सकते हैं:

// Returns the absolute position of an entity as the sum
// of its own position and the position of its ancestors.
// `obj` should have `position` and `parent` methods.
template <class Entity>
Vec2i abs_position(Entity& obj)
{
     const Vec2i xy = obj.xy();
     if (obj.parent())
     {
         // If the entity has a parent, return the sum of the parent 
         // position and the entity's local position.
         return xy + abs_position(obj.parent());
     }
     return xy;
}

... निश्चित रूप से ऐसे मामले में, parentविधि को उसी Entityप्रकार वापस करना होगा , जिस स्थिति में हम संभवत: इंटरफेस से बचना चाहते हैं (क्योंकि वे अक्सर आधार बिंदुओं के साथ काम करने के पक्ष में प्रकार की जानकारी खोना चाहते हैं)।

इकाई-घटक प्रणाली

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

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

व्यावहारिक दृष्टिकोण

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


11

यह एक निर्णय कॉल है जिसे आपको केस-बाय-केस आधार पर करना है।

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

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

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

निम्नलिखित कोड पर विचार करें:

public interface A
{
    void X();
    void Y();
}

public class Foo
{
     public void ConsumeX(A a)
     {
         a.X();
     }
}

अब हमारे पास एक ऐसी स्थिति है, जहां अगर हम कंस्यूमएक्स को एक नई वस्तु देना चाहते हैं, तो उसे अनुबंध को फिट करने के लिए एक्स () और वाई () को लागू करना होगा।

तो क्या हमें अगले उदाहरण की तरह दिखने के लिए, अभी कोड बदलना चाहिए?

public interface A
{
    void X();
    void Y();
}

public interface B
{
    void X();
}

public class Foo
{
     public void ConsumeX(B b)
     {
         b.X();
     }
}

आईएसपी हमें सुझाव देता है, इसलिए हमें उस निर्णय की ओर झुकना चाहिए। लेकिन, संदर्भ के बिना, यह सुनिश्चित करना मुश्किल है। क्या यह संभव है कि हम A और B का विस्तार करेंगे? क्या यह संभावना है कि वे स्वतंत्र रूप से विस्तार करेंगे? क्या यह संभावना है कि बी कभी ऐसे तरीकों को लागू करेगा जिनकी ए को आवश्यकता नहीं है? (यदि नहीं, तो हम A को B से प्राप्त कर सकते हैं)

यह निर्णय कॉल आपको करना है। और, यदि आपके पास वास्तव में उस कॉल को करने के लिए पर्याप्त जानकारी नहीं है, तो आपको संभवतः सबसे सरल विकल्प लेना चाहिए, जो कि पहले कोड हो सकता है।

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


1
"सबसे पहले, याद रखें कि एसओएलआईडी सिद्धांत सिर्फ यही हैं ... सिद्धांत। वे नियम नहीं हैं। वे एक चांदी की गोली नहीं हैं। वे सिर्फ सिद्धांत हैं। यह उनके महत्व से दूर नहीं है, आपको हमेशा झुकना चाहिए। उनका अनुसरण करने की ओर। लेकिन दूसरा वे दर्द के स्तर का परिचय देते हैं, आपको उन्हें तब तक खोदना चाहिए जब तक आपको उनकी आवश्यकता नहीं है। " यह हर डिज़ाइन पैटर्न / सिद्धांतों की किताब के पहले पृष्ठ पर होना चाहिए। इसे रिमाइंडर के रूप में प्रत्येक 50 पृष्ठों पर भी दिखाई देना चाहिए।
क्रिश्चियन रोड्रिगेज
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.