2010-04-06 12 views
15

यहाँ मेरी कोड है। की तरह कुछ:वस्तु उन्मुख डिजाइन सुझाव

soldier.gun.fire(); 

या

soldier.getGun().load(15); 

तो जो एक एक बेहतर डिजाइन है? बंदूक वस्तु को एक निजी सदस्य के रूप में छुपाएं और getGun() फ़ंक्शन के साथ इसे एक्सेस करें। या इसे एक सार्वजनिक सदस्य बनाते हैं? या मैं इन सभी कार्यों को कार्यान्वित कर सकता हूं कार्यान्वयन को कठिन बना देगा:

soldier.loadGun(15); // calls Gun.load() 
soldier.fire(); // calls Gun.fire() 

तो आपको कौन सा लगता है सबसे अच्छा है?

+3

स्टीफन और फ्रस्ट्रेटेड से जवाब एकत्र करें। एक निजी 'get_gun()' है जो सैनिक के लिए बंदूक प्राप्त करने के लिए जरूरी काम करता है, जैसा कि स्टीफन दिखाता है, लेकिन * सैनिक * बताएं कि क्या करना है, न कि बंदूक, निराशाजनक शो के रूप में। – GManNickG

+0

@GMan - धन्यवाद, मैं सहमत हूं। हालांकि, @ फ्रस्ट्रेटेड के दृष्टिकोण की बजाय, सैनिक को बताएं कि क्या करना है @ ऑस्टिन का दृष्टिकोण? जैसे "soldier.loadGun()" के बजाय सैनिक। एटैक()। – Stephen

+0

@ स्टीफन: हाँ, आप encapsulation को जितना चाहें उतना गहरा कर सकते हैं। मेरा सवाल केवल इस हिस्से के बारे में था। बेशक आप सैनिक कह सकते हैं। एटैक() और इस फ़ंक्शन के अंदर आप जांच सकते हैं कि (! Gun.getBullets()) वापसी या उस तरह की चीज़ें। – pocoa

उत्तर

21

मैं अपने दूसरे विकल्प के साथ जाना कहेंगे:

soldier.loadGun(15); // calls Gun.load() 
soldier.fire(); // calls Gun.fire() 

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

+3

+1 सैनिक वर्ग के आपके विवरण के लिए +1: पी –

+1

मुझे आपके उदाहरण पसंद हैं :) – Dinah

+2

इसके अलावा, loadGun मत कहो, readyWeapon कहें। इस तरह जब आपका सैनिक टैंक में होता है, तो वह टैंक तोप घूमने पर अपने पिस्तौल से फंसे नहीं होता है। – jmucchiello

1

कोई सुनहरा नियम नहीं है जो 100% समय लागू होता है। यह वास्तव में आपकी जरूरतों के आधार पर एक निर्णय कॉल है।

यह इस बात पर निर्भर करता है कि बंदर के लिए सॉलिडर तक पहुंच से आप कितनी कार्यक्षमता को छिपाना/अस्वीकार करना चाहते हैं।

यदि आप केवल गन तक पहुंच को पढ़ना चाहते हैं तो आप अपने सदस्य के लिए एक कॉन्स्ट संदर्भ वापस कर सकते हैं।

यदि आप केवल कुछ कार्यक्षमता का पर्दाफाश करना चाहते हैं तो आप रैपर फ़ंक्शन बना सकते हैं। यदि आप नहीं चाहते हैं कि उपयोगकर्ता सैनिक के माध्यम से गन सेटिंग्स को बदलने का प्रयास करें तो रैपर फ़ंक्शन बनाएं।

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

शायद आप बंदूक की प्रतिलिपि नहीं चाहते हैं, इसलिए यदि आप गेटगुन() विधि बनाते हैं तो सुनिश्चित करें कि आप बंदूक की एक प्रति वापस नहीं कर रहे हैं।

यदि आप अपना कोड सरल रखना चाहते हैं तो बंदूक से निपटने के लिए सैनिक को जिम्मेदार ठहराएं। क्या आपके अन्य कोड को सीधे बंदूक के साथ काम करने की ज़रूरत है? या क्या एक सैनिक हमेशा जानता है कि अपनी बंदूक कैसे काम/पुनः लोड करें?

+0

बंदूक उपर्युक्त उदाहरण में निजी है, इसे या तो गेटगुन() – Fermin

+0

@Brian जैसे लिखित सार्वजनिक या एक्सेसर विधि बनाने की आवश्यकता होगी: मुझे सैनिक से गन ऑब्जेक्ट के सभी सार्वजनिक सदस्यों तक पहुंचने की आवश्यकता है। – pocoa

+0

@pocoa: फिर GetGun() के माध्यम से इसका संदर्भ दें या बस बंदूक सार्वजनिक करें। यह शायद सबसे आसान है। जब आप अपने गन इंटरफ़ेस में बाद में परिवर्तन करना चाहते हैं तो आपको अपने सैनिक वर्ग 'इंटरफ़ेस को भी बदलने की आवश्यकता नहीं होगी। –

1

"getGun()" या बस "बंदूक()" प्रदान करें।

एक दिन कल्पना कीजिए कि आप उस विधि को और अधिक जटिल बनाने की जरूरत है हो सकता है:

Gun* getGun() { 
    if (!out_of_bullets_) { 
    return &gun_; 
    } else { 
    PullPieceFromAnkle(); 
    return &secret_gun_; 
    } 
} 

इसके अलावा, आप एक स्थिरांक एक्सेसर प्रदान करने के लिए ताकि लोगों को एक स्थिरांक सैनिक पर एक स्थिरांक बंदूक का उपयोग कर सकते कर सकते हैं:

const Gun &getGun() const { return gun_; } 
7

डेमेटर का कानून कार्यों को समाहित करने के लिए कहता है।

http://en.wikipedia.org/wiki/Law_of_Demeter

इस तरह, यदि आप सैनिक और बंदूक के बीच बातचीत का कुछ प्रकार चाहते हैं, आप कोड डालने के लिए एक जगह है।

संपादित करें: विकिपीडिया लिंक से प्रासंगिक लेख मिला: http://www.ccs.neu.edu/research/demeter/demeter-method/LawOfDemeter/paper-boy/demeter.pdf paperboy के उदाहरण बहुत, बहुत सैनिक उदाहरण आप पोस्ट करने के लिए समान है।

+3

और "कम डॉट गिनती के कानून" में आने से बचने के लिए ध्यान रखें (http://haacked.com/archive/2009/07/14/law-of-demeter-dot-counting.aspx) –

+0

ग्रेट लिंक मार्टिनो ! पूरी तरह से एलओडी के बारे में मेरी भावनाओं को बताता है। –

+0

इस मामले में, "गन" दिया गया एक निर्माता पैरामीटर है, मुझे नहीं लगता कि यह लागू होता है। यह encapsulation तोड़ नहीं है क्योंकि क्लाइंट क्लास के साथ शुरू करने के लिए बंदूक को तत्काल करने की जरूरत है। यह कहना नहीं है कि एक सैनिक :: हमला (लक्ष्य * टी) विधि एक बेहतर दृष्टिकोण नहीं है। – Stephen

5

वास्तव में, यह इस बात पर निर्भर करता है कि आप कितना नियंत्रण रखना चाहते हैं।

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

11

सबसे पहले लक्ष्य पर बंदूक फेंक और, दूर चला करता है, तो अपर्याप्त गोला बारूद या तो ऑपरेशन के लिए मौजूद थे ... तुम Soldier वर्ग बाहर से Gun एक्सेस करके Law of Demeter का उल्लंघन होगी।

मैं इन बजाय तरह के तरीकों पर विचार करेंगे:

soldier.ArmWeapon(...); 
soldier.Attack(...); 

इस तरह आप भी अपने मुट्ठी, चाकू, ग्रेनेड, बेसबॉल के बल्ले, लेजर बिल्ली को लागू कर सकता है, आदि

2

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

Btw, Bjarne Stroustrup अपनी साइट में इस मुद्दे के बारे में थोड़ा बात करती है: http://www.artima.com/intv/goldilocks3.html

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

+0

नहीं, सैनिक सिर्फ एक कंटेनर वर्ग नहीं है। लिंक के लिए धन्यवाद। – pocoa

3

आप बंदूक का पर्दाफाश, तो आप गन, जो शायद एक अच्छा विचार नहीं है के सदस्य कार्यों से परे बातें अनुमति देते हैं:

soldier.gun = anotherGun; // where did you drop your old gun? 

आप getGun() का उपयोग करते हैं, तो कॉल एक छोटे बदसूरत देखो, लेकिन आप सैनिक को संशोधित किए बिना गन में फ़ंक्शंस जोड़ सकते हैं।

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

0

यदि आप बाद में तर्क बदलते हैं तो भी एक सतत UI प्रदान करने के लिए कार्यों को Encapsulate करें।नामकरण सम्मेलन आपके ऊपर हैं, लेकिन मैं आमतौर पर "getFoo()" का उपयोग नहीं करता, लेकिन केवल "foo()" एक्सेसर्स के रूप में और "setFoo()" सेटर्स के रूप में।

  • जब आप कर सकते हैं संदर्भ संदर्भ-टू-कॉन्स (प्रभावी सी ++ आइटम # 3)।
  • कठिन कोडित नंबर का उपयोग कर (मद 4 #)
  • अपने निजी सदस्यों के लिए अद्वितीय नामकरण सम्मेलनों उन्हें तर्क से अलग करने के
  • उपयोग अहस्ताक्षरित मूल्यों जहां वे भावना करने के लिए त्रुटियों को स्थानांतरित करने के बनाने प्रदान करने के लिए consts, enums, और inlines पसंद करते हैं संकलन समय
  • जब अधिकतम मानों की तरह मानों को मानते हैं, तो पूरे वर्ग पर लागू होते हैं। उन्हें स्थिर बनाओ।
  • आप के वारिस की योजना है, सुनिश्चित करें कि आपके विनाशकर्ता आभासी
  • समझदार चूक

यह वह जगह है क्लासेस की देखभाल कैसे करने के लिए सभी सदस्यों को प्रारंभ कर रहे हैं। CodePad

#include <iostream> 
#include <string> 
#include <stdint.h> 

using namespace std; 

class Gun 
{ 
public: 
    Gun() : _bullets(0) {} 
    virtual ~Gun() {} 
    void fire() {cout << "bang bang" << endl; _bullets--;} 
    void load(const uint16_t bullets) {_bullets = bullets;} 
    const int bullets() const {return _bullets;} 

    static const uint16_t MAX_BULLETS = 17; 

protected: 
    int _bullets; 
}; 

class Soldier 
{ 
public: 
    Soldier(const string &name, const Gun &gun) : _name(name), _gun(gun) {} 
    virtual ~Soldier() {} 
    const string& name() const; 
    Gun& gun() {return _gun;} 

protected: 
    string _name; 
    Gun _gun; 
}; 


int main (int argc, char const *argv[]) 
{ 
    Gun gun; // initialize 
    string name("Foo"); 
    Soldier soldier(name, gun); 

    soldier.gun().load(Gun::MAX_BULLETS); 

    for(size_t i = 0; i < Gun::MAX_BULLETS; ++i) 
    { 
    soldier.gun().fire(); 
    cout << "I have " << soldier.gun().bullets() << " left!" << endl; 
    } 
    return 0; 
} 
संबंधित मुद्दे