2010-04-11 15 views
6

क्या यह "नीति" वर्गों (जैसे रणनीति वर्ग) के लिए बहुत खराब नीति है, केवल एक चीज है?रणनीति पैटर्न और "कार्रवाई" कक्षा विस्फोट

मान लीजिए कि मैं एक राक्षस वर्ग बनाना चाहता हूं। एक वर्ग में राक्षस के बारे में जो कुछ भी मैं चाहता हूं उसे परिभाषित करने के बजाय, मैं यह पहचानने की कोशिश करूंगा कि इसकी मुख्य विशेषताएं क्या हैं, इसलिए मैं उन्हें इंटरफेस में परिभाषित कर सकता हूं। इससे:

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

कॉन: यह मॉडल कठोर है। कभी-कभी हम ऐसा कुछ परिभाषित करना चाहते हैं जो आसानी से इस "बिल्डिंग ब्लॉक" को एक साथ रखने की कोशिश करके हासिल नहीं किया जाता है।

public class AlienMonster : IWalk, IRun, ISwim, IGrowl { 
    IWalkStrategy _walkStrategy; 
    IRunStrategy _runStrategy; 
    ISwimStrategy _swimStrategy; 
    IGrowlStrategy _growlStrategy; 

    public Monster() { 
     _walkStrategy = new FourFootWalkStrategy(); 
     ...etc 
    } 

    public void Walk() { _walkStrategy.Walk(); } 
    ...etc 
} 

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

इस मामले पर आपकी राय क्या है? क्या यह एक वैध तर्क है? क्या यह ओवर-इंजीनियरिंग है?

इसके अलावा

, यह मानते हुए हम उदाहरण मैं ऊपर दे दी है के लिए उचित समायोजन करना चाहते हैं, यह के रूप में IWalk परिभाषित करने के लिए बेहतर होगा:

interface IWalk { 
    void Walk(); 
} 

या

interface IWalk { 
    IWalkStrategy WalkStrategy { get; set; } //or something that ressembles this 
} 

जा रहा है कि इस मैं कर रहा मॉन्स्टर पर विधियों को परिभाषित करने की आवश्यकता नहीं होगी, मेरे पास सिर्फ IWalkStrategy के लिए सार्वजनिक गेटर्स होंगे (ऐसा लगता है कि आपको जितना संभव हो उतना सब कुछ encapsulate करना चाहिए!) क्यों?

धन्यवाद

उत्तर

2

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

गुर्राना एक iability इंटरफ़ेस की तरह कुछ के एक कार्यान्वयन हो सकता है। और एक विशेष राक्षस में IAbility कार्यान्वयन का संग्रह हो सकता है। IMove, IACT उदाहरण के लिए:

फिर इंटरफेस की एक जोड़ी जो क्षमता या हरकत का उपयोग करने के तर्क है कि है।

public class AlienMonster : IMove, IAct 
{ 
    private List<IAbility> _abilities; 
    private List<ILocomotion> _locomotions; 

    public AlienMonster() 
    { 
    _abilities = new List<IAbility>() {new Growl()}; 
    _locomotion = new List<ILocomotion>() {new Walk(), new Run(), new Swim()} 
    } 

    public void Move() 
    { 
    // implementation for the IMove interface 
    } 

    public void Act() 
    { 
    // implementation for the IAct interface 
    } 

} 

अपनी कक्षा को इस तरह से लिखकर आप कठोरता से बचेंगे।

संपादित करें:

संपादित IMove और IACT के बारे में सामान कहा: कुछ टिप्पणियाँ

के बाद एक राक्षस आप कह रहे हैं कि कुछ भी देख सकते हैं वस्तु में सक्षम होना चाहिए करने के लिए IWalk, इरुन, और ISwim जोड़ कर उन किसी भी इंटरफेस में लागू किसी भी तरीके को कॉल करें और यह सार्थक हो। आगे यह तय करने के लिए कि तीन इंटरफेस का उपयोग किस प्रकार करना चाहिए, आपको पूरे ऑब्जेक्ट को पास करना होगा। एक इंटरफेस का उपयोग करने का एक बड़ा फायदा यह है कि आप इसे उस इंटरफ़ेस द्वारा संदर्भित कर सकते हैं।

void SomeFunction(IWalk alienMonster) {...} 

ऊपर समारोह कुछ भी है कि IWalk लागू करता है ले जाएगा, लेकिन उन में से प्रत्येक के लिए एक पूरी नई समारोह लिख सकते हैं या पूरे में AlienMonster वस्तु पारित करने के लिए अगर वहाँ इरुन, ISwim के लिए SomeFunction के रूपांतरों हैं, और इसलिए आप पर है । यदि आप ऑब्जेक्ट को पास करते हैं तो वह फ़ंक्शन किसी भी और सभी इंटरफेस को कॉल कर सकता है। इसका यह भी अर्थ है कि उस कार्य को एलियन मॉन्स्टर ऑब्जेक्ट से यह पूछने के लिए पूछना है कि इसकी क्षमताओं क्या हैं और फिर तय करें कि किस का उपयोग करना है। यह सब बाहरी कार्यक्षमता को समाप्त करने के लिए समाप्त होता है जिसे कक्षा में आंतरिक रखा जाना चाहिए। क्योंकि आप उन सभी को बाहरी कर रहे हैं और IWalk, इरुन, इस्विम के बीच समानता नहीं है, इसलिए कुछ फ़ंक्शन निर्दोष रूप से सभी तीन इंटरफेस को कॉल कर सकते हैं और आपका राक्षस एक ही समय में चलने वाली तैराकी कर सकता है।इसके बाद से आप कुछ कक्षाओं पर आईवॉक, इरुन, इस्विम को कॉल करने में सक्षम होना चाहते हैं, सभी वर्गों को मूल रूप से सभी तीन इंटरफेस लागू करना होगा और आप एक राक्षस के लिए आईएसविम के लिए इंटरफ़ेस आवश्यकता को पूरा करने के लिए कैनोटस्विम जैसी रणनीति तैयार कर देंगे तैर नहीं सकते अन्यथा आप राक्षस पर लागू नहीं होने वाले इंटरफ़ेस को कॉल करने का प्रयास कर सकते हैं। अंत में आप वास्तव में अतिरिक्त इंटरफेस, आईएमओ के लिए कोड खराब कर रहे हैं।

+1

इस तरह से करने के बारे में एक और अच्छी बात यह है कि, जब आप ए से बी में जाना चाहते हैं, तो आप केवल लोकोमोशन इंटरफेस की अपनी सूची में फिर से सक्रिय हो सकते हैं और हार्डकोडिंग की बजाय प्रत्येक लागत से पूछ सकते हैं "चलने की लागत प्राप्त करें; रन लागत प्राप्त करें ; तैरने की लागत पाएं; फ्लाई लागत प्राप्त करें; टेलीपोर्ट लागत प्राप्त करें; ... "और उस सूची को अद्यतन रखने के तकनीकी ऋण को लेकर नए लोकोमोशन प्रकार लागू किए गए हैं। –

+0

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

+1

मुझे दोबारा दोहराएं "इंटरफेस वर्णन नहीं करता कि प्रति कुछ क्या कर सकता है।" तार्किक और लगातार फैशन में इंटरफेस का उपयोग करना एक अच्छा विचार है। Growl के लिए ILocomotion का उपयोग करना एक बुरा विकल्प होगा। लेकिन चलना, तैरना, दौड़ना, उड़ना, टेलीपोर्ट, और इसी तरह सभी आंदोलन के रूप हैं। उन्हें एक आंदोलन इंटरफेस में सारण करके आप अपने कोड को बहुत सरल बना सकते हैं। आपके पास इलोकोमोशन का 100 कार्यान्वयन हो सकता है और 20 आईवॉक, 20 इरुन, 20 इस्विम, 20 आईस्किप, 2 आईटेलेपोर्ट, 18 आईजंप कार्यान्वयन से बेहतर हो सकता है। अगर कोई इलोकोमोशन इंटरफ़ेस फिट नहीं करता है तो एक नया इंटरफ़ेस बनाएं। – Felan

2

भाषाओं में जो एकाधिक वंशानुक्रम समर्थन करते हैं, आप वास्तव में वर्ग है जो चीजों को यह कर सकते हैं का प्रतिनिधित्व से इनहेरिट करके अपने Monster बना सकते हैं। इन कक्षाओं में, आप इसके लिए कोड लिखेंगे, ताकि कक्षाओं को कार्यान्वित करने के बीच कोई कोड कॉपी नहीं किया जा सके।

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

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

तो संक्षेप में: यदि आप कक्षाओं के बीच कॉपी किए गए कोड को कम करते हैं तो अतिरिक्त I...Strategy दृष्टिकोण का उपयोग करके, जैसा कि आपने प्रस्तावित किया था, वही करता हूं।

1

"क्या ढूँढ़े भिन्न होता है और यह संपुटित।"

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

सामान्य में: नहीं, यह निश्चित रूप से नहीं से अधिक इंजीनियरिंग अपने स्वयं के वर्गों में विभिन्न अवधारणाओं को संपुटित है। वहाँ भी ठोस वर्ग sealed या final, या तो बनाने के साथ कुछ भी गलत नहीं है। यह लोगों बलों बूझकर कुछ है कि शायद नहीं विरासत में मिला दिया जाना चाहिए से इनहेरिट से पहले कैप्सूलीकरण तोड़ने के लिए।

2

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

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

संबंधित मुद्दे