डॉक्टर ब्राउन के जवाब में लॉ ऑफ़ डेमेटर का एक क्लासिक पाठ्यपुस्तक कार्यान्वयन दिखाया गया है - और दर्जनों तरीकों को जोड़ने की झुंझलाहट / अव्यवस्थित-कोड-ब्लोट, शायद इसीलिए प्रोग्रामर, खुद को शामिल करते हैं, अक्सर ऐसा करने से परेशान नहीं होते हैं, भले ही उन्हें ऐसा करना पड़े।
वस्तुओं के पदानुक्रम को कम करने का एक वैकल्पिक तरीका है:
अपने तरीकों और गुणों के माध्यम से, प्रकारों के interfaceबजाय, प्रकारों को उजागर classकरें।
मूल पोस्टर (ओपी) के मामले में, के बजाय encoder->WaitEncoderFrame()वापस आ जाएगा , और परिभाषित करेगा कि क्या संचालन स्वीकार्य हैं।IEncoderFrameFrame
समाधान 1
सबसे आसान मामले में, Frameऔर Encoderकक्षाएं दोनों आपके नियंत्रण में हैं, IEncoderFrameविधियों का एक सबसेट है जो पहले से ही सार्वजनिक रूप से फ़्रेम करता है, और Encoderवर्ग वास्तव में परवाह नहीं करता है कि आप उस वस्तु का क्या करते हैं। फिर, कार्यान्वयन तुच्छ है ( c # में कोड ):
interface IEncoderFrame {
void DoOrGetSomething();
}
class Frame : IEncoderFrame {
// A method that already exists in Frame.
public void DoOrGetSomething() { ... }
}
class Encoder {
private Frame _frame;
public IEncoderFrame TheFrame { get { return _frame; } }
...
}
समाधान २
एक मध्यवर्ती मामले में, जहां Frameपरिभाषा आपके नियंत्रण में नहीं है, या इसके IEncoderFrameतरीकों को जोड़ना उचित नहीं होगा Frame, तो एक अच्छा समाधान एक एडेप्टर है । यही कारण है कि CandiedOrange के उत्तर पर चर्चा की जाती है new FrameHandler( frame )। महत्वपूर्ण: यदि आप ऐसा करते हैं, तो यह अधिक लचीला है यदि आप इसे एक इंटरफ़ेस के रूप में उजागर करते हैं , एक वर्ग के रूप में नहीं । Encoderके बारे में पता है class FrameHandler, लेकिन ग्राहकों को केवल जानने की जरूरत है interface IFrameHandler। या जैसा कि मैंने इसे नाम दिया है, interface IEncoderFrame- यह इंगित करने के लिए कि यह विशेष रूप से फ़्रेम है जैसा कि एन्कोडर के पीओवी से देखा गया है :
interface IEncoderFrame {
void DoOrGetSomething();
}
// Adapter pattern. Appropriate if no access needed to Encoder.
class EncoderFrameWrapper : IEncoderFrame {
Frame _frame;
public EncoderFrameWrapper( Frame frame ) {
_frame = frame;
}
public void DoOrGetSomething() {
_frame....;
}
}
class Encoder {
private Frame _frame;
// Adapter pattern. Appropriate if no access needed to Encoder.
public IEncoderFrame TheFrame { get { return new EncoderFrameWrapper( _frame ); } }
...
}
COST: एक नई वस्तु का आवंटन और GC, एनकोडरफ्रेमवर्पर, हर बार encoder.TheFrameकहा जाता है। (आप उस आवरण को कैश कर सकते हैं, लेकिन वह अधिक कोड जोड़ता है। और केवल मज़बूती से कोड करना आसान है यदि एनकोडर के फ़्रेम फ़ील्ड को एक नए फ्रेम के साथ प्रतिस्थापित नहीं किया जा सकता है।)
समाधान ३
अधिक कठिन मामले में, नए रैपर को दोनों के बारे में जानना होगा Encoderऔर Frame। वह वस्तु स्वयं LoD का उल्लंघन करेगी - यह एनकोडर और फ़्रेम के बीच एक संबंध में हेरफेर कर रहा है जो एनकोडर की जिम्मेदारी होनी चाहिए - और शायद सही होने के लिए दर्द हो। यदि आप उस सड़क को शुरू करते हैं तो क्या हो सकता है:
interface IEncoderFrame {
void DoOrGetSomething();
}
// *** You will end up regretting this. See next code snippet instead ***
class EncoderFrameWrapper : IEncoderFrame {
Encoder _owner;
Frame _frame;
public EncoderFrameWrapper( Encoder owner, Frame frame ) {
_owner = owner; _frame = frame;
}
public void DoOrGetSomething() {
_frame.DoOrGetSomething();
// Hmm, maybe this wrapper class should be nested inside Encoder...
_owner... some work inside owner; maybe should be owner-internal details ...
}
}
class Encoder {
private Frame _frame;
...
}
वह बदसूरत हो गया। एक कम-जटिल कार्यान्वयन है, जब आवरण को अपने निर्माता / मालिक (एनकोडर) के विवरण को छूने की आवश्यकता होती है:
interface IEncoderFrame {
void DoOrGetSomething();
}
class Encoder : IEncoderFrame {
private Frame _frame;
// HA! Client gets to think of this as "the frame object",
// but its really me, intercepting it.
public IEncoderFrame TheFrame { get { return this; } }
// This is the method that the LoD approach suggests writing,
// except that we are exposing it only when the instance is accessed as an IEncoderFrame,
// to avoid extending Encoder's already large API surface.
public void IEncoderFrame.DoOrGetSomething() {
_frame.DoOrGetSomething();
... make some change within current Encoder instance ...
}
...
}
दी, अगर मुझे पता था कि मैं यहाँ समाप्त हो जाएगा, तो मैं ऐसा नहीं कर सकता। बस एलओडी के तरीके लिख सकते हैं, और इसके साथ किया जा सकता है। इंटरफ़ेस को परिभाषित करने की आवश्यकता नहीं है। दूसरी ओर, मुझे यह पसंद है कि इंटरफ़ेस संबंधित तरीकों को एक साथ लपेटता है। मुझे यह पसंद है कि "फ्रेम जैसा संचालन" करने के लिए कैसा महसूस होता है जो फ्रेम की तरह महसूस होता है।
अंतिम टिप्पणियाँ
इस पर विचार करें: यदि लागू करने वाले ने Encoderमहसूस किया कि एक्सपोज़िंग Frame frameउनकी समग्र वास्तुकला के लिए उपयुक्त था, या "LoD को लागू करने की तुलना में बहुत आसान था", तो यह ज्यादा सुरक्षित होता अगर वे पहले स्निपेट I शो करते - एक सीमित उपसमूह को बेनकाब करते। फ़्रेम, एक इंटरफ़ेस के रूप में। मेरे अनुभव में, यह अक्सर एक पूरी तरह से व्यावहारिक समाधान है। बस जरूरत के अनुसार इंटरफेस में तरीके जोड़ें। (मैं एक ऐसे परिदृश्य के बारे में बात कर रहा हूं जहां हम "जानते हैं" फ़्रेम में पहले से ही आवश्यक विधियां हैं, या वे जोड़ना आसान और गैर-विवादास्पद होगा। प्रत्येक विधि के लिए "कार्यान्वयन" कार्य इंटरफ़ेस परिभाषा में एक पंक्ति जोड़ रहा है।) और पता है कि सबसे खराब भविष्य के परिदृश्य में भी, एपीआई को काम पर रखना संभव है - यहाँ,IEncoderFrameFrameEncoder।
यह भी ध्यान रखें कि यदि आप को जोड़ने के लिए अनुमति नहीं है IEncoderFrameकरने के लिए Frame, या जरूरत तरीकों सामान्य करने के लिए अच्छी तरह से फिट नहीं है Frameवर्ग, और समाधान # 2 शायद अतिरिक्त ऑब्जेक्ट निर्माण और विनाश के कारण, आप सूट नहीं करता है, समाधान # 3 को Encoderएलओडी को पूरा करने के तरीकों को व्यवस्थित करने के लिए बस एक तरीके के रूप में देखा जा सकता है । सिर्फ दर्जनों तरीकों से न गुजरें। उन्हें एक इंटरफ़ेस में लपेटें, और "स्पष्ट इंटरफ़ेस कार्यान्वयन" (यदि आप c # में हैं) का उपयोग करें, ताकि उन्हें केवल उस इंटरफ़ेस के माध्यम से देखे जाने पर एक्सेस किया जा सके।
एक और बात जिस पर मैं जोर देना चाहता हूं , वह यह है कि एक इंटरफ़ेस के रूप में कार्यक्षमता को उजागर करने का निर्णय ऊपर वर्णित सभी स्थितियों को संभालता है। पहले में, IEncoderFrameबस Frameकार्यक्षमता का एक सबसेट है । दूसरे में, IEncoderFrameएक एडेप्टर है। तीसरे में, एस कार्यक्षमता IEncoderFrameमें एक विभाजन है Encoder। इससे कोई फर्क नहीं पड़ता कि आपकी ज़रूरतें इन तीन स्थितियों के बीच बदलती हैं: एपीआई एक ही रहता है।