निजी डेटा तक पहुंचने के लिए निजी पद्धति को सार्वजनिक मार्ग कब लेना चाहिए?


11

निजी डेटा तक पहुंचने के लिए निजी पद्धति को सार्वजनिक मार्ग कब लेना चाहिए? उदाहरण के लिए, यदि मेरे पास यह अपरिवर्तनीय 'गुणक' वर्ग है (थोड़ा-सा विरोध, मुझे पता है):

class Multiplier {
public:
    Multiplier(int a, int b) : a(a), b(b) { }
    int getA() const { return a; }
    int getB() const { return b; }
    int getProduct() const { /* ??? */ }
private:
    int a, b;
};

मेरे लागू करने के दो तरीके हैं getProduct:

    int getProduct() const { return a * b; }

या

    int getProduct() const { return getA() * getB(); }

क्योंकि यहाँ इरादा के मूल्य का उपयोग aकरने के लिए है a , प्राप्त करने के लिए , का उपयोग getA()करने के getProduct()लिए मुझे साफ लगता है। aजब तक मुझे इसे संशोधित नहीं करना पड़ता, मैं उपयोग करने से बचना पसंद करूंगा । मेरी चिंता यह है कि मैं अक्सर इस तरह से लिखे गए कोड को नहीं देखता, मेरे अनुभव में a * bइससे अधिक सामान्य कार्यान्वयन होगा getA() * getB()

क्या निजी तरीकों को कभी भी सार्वजनिक तरीके का उपयोग करना चाहिए जब वे सीधे किसी चीज़ तक पहुंच सकते हैं?

जवाबों:


7

यह के वास्तविक अर्थ की निर्भर करता है a, bऔर getProduct

गेटर्स का उद्देश्य ऑब्जेक्ट के इंटरफ़ेस को समान रखते हुए वास्तविक कार्यान्वयन को बदलने में सक्षम होना है। उदाहरण के लिए, यदि एक दिन, getAबन जाता है return a + 1;, तो परिवर्तन एक गटर के लिए स्थानीय होता है।

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

यदि यह getProductउपयोग करने के लिए aऔर bसीधे के लिए एकदम सही समझ में आता है , तो उनका उपयोग करें। अन्यथा, बाद में रखरखाव के मुद्दों को रोकने के लिए गेटर्स का उपयोग करें।

उदाहरण जहां एक गेटर्स का उपयोग करेगा:

class Product {
public:
    Product(ProductId id) : {
        price = Money.fromCents(
            data.findProductById(id).price,
            environment.currentCurrency
        )
    }

    Money getPrice() {
        return price;
    }

    Money getPriceWithRebate() {
        return getPrice().applyRebate(rebate); // ← Using a getter instead of a field.
    }
private:
    Money price;
}

इस समय के लिए, गेट्टर में कोई व्यावसायिक तर्क नहीं है, यह बाहर नहीं रखा गया है कि निर्माणकर्ता के तर्क को ऑब्जेक्ट को इनिशियलाइज़ करते समय डेटाबेस के काम से बचने के लिए गेट्टर में माइग्रेट किया जाएगा:

class Product {
public:
    Product(ProductId id) : id(id) { }

    Money getPrice() {
        return Money.fromCents(
            data.findProductById(id).price,
            environment.currentCurrency
        )
    }

    Money getPriceWithRebate() {
        return getPrice().applyRebate(rebate);
    }
private:
    const ProductId id;
}

बाद में, कैशिंग जोड़ा जा सकता है (सी # में, एक का उपयोग करेगा Lazy<T>, कोड को छोटा और आसान बना देगा; मुझे नहीं पता कि क्या सी ++ में एक बराबर है):

class Product {
public:
    Product(ProductId id) : id(id) { }

    Money getPrice() {
        if (priceCache == NULL) {
            priceCache = Money.fromCents(
                data.findProductById(id).price,
                environment.currentCurrency
            )

        return priceCache;
    }

    Money getPriceWithRebate() {
        return getPrice().applyRebate(rebate);
    }
private:
    const ProductId id;
    Money priceCache;
}

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

उदाहरण जहां कोई निजी क्षेत्रों का उपयोग करेगा:

class Product {
public:
    Product(ProductId id) : id(id) { }
    ProductId getId() const { return id; }
    Money getPrice() {
        return Money.fromCents(
            data.findProductById(id).price, // ← Accessing `id` directly.
            environment.currentCurrency
        )
    }
private:
    const ProductId id;
}

गेट्टर सीधा है: यह एक निरंतर (C # 's readonly) क्षेत्र के समान प्रत्यक्ष प्रतिनिधित्व है , जिसे भविष्य में बदलने की उम्मीद नहीं है: संभावना है, आईडी गेट्टर कभी भी एक गणना मूल्य नहीं बनेगा। इसलिए इसे सरल रखें, और सीधे क्षेत्र तक पहुंचें।

एक और लाभ यह है कि getIdभविष्य में हटाया जा सकता है अगर ऐसा प्रतीत होता है कि इसका उपयोग बाहर नहीं किया गया है (जैसे कि कोड के पिछले टुकड़े में)।


मैं आपको +1 नहीं दे सकता क्योंकि निजी क्षेत्रों का उपयोग करने के लिए आपका उदाहरण एक IMHO नहीं है, इसका मुख्य कारण यह है कि आपने घोषणा की है const: मेरा मानना ​​है कि कंपाइलर का मतलब getIdवैसे भी कॉल को इनलाइन करना होगा और यह आपको किसी भी दिशा में बदलाव करने की अनुमति देता है। (अन्यथा मैं गेटर्स का उपयोग करने के लिए आपके कारणों से पूरी तरह सहमत हूं ।) और ऐसी भाषाएं जो संपत्ति सिंटैक्स प्रदान करती हैं, सीधे समर्थन क्षेत्र के बजाय संपत्ति का उपयोग नहीं करने के और भी कम कारण हैं।
मार्क हर्ड

1

आमतौर पर, आप सीधे चर का उपयोग करेंगे। क्लास के कार्यान्वयन को बदलते समय आप सभी सदस्यों को बदलने की उम्मीद करते हैं। सीधे चर का उपयोग नहीं करने से कोड को सही ढंग से अलग करना अधिक कठिन हो जाता है जो उन पर निर्भर करता है और सदस्य को पढ़ना कठिन बना देता है।

यह निश्चित रूप से अलग है अगर गेटर्स वास्तविक तर्क को लागू करते हैं, तो उस स्थिति में यह इस बात पर निर्भर करता है कि आपको उनके तर्क का उपयोग करने की आवश्यकता है या नहीं।


1

मैं कहूंगा कि सार्वजनिक तरीकों का उपयोग करना बेहतर होगा, यदि किसी अन्य कारण से नहीं बल्कि DRY के अनुरूप हो ।

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


0

एक वर्ग के लिए यह छोटा, सरलता जीतता है। मैं सिर्फ एक * b का उपयोग करूंगा।

कुछ अधिक जटिल के लिए, मैं दृढ़ता से getA () * getB () का उपयोग करने पर विचार करूंगा अगर मैं स्पष्ट रूप से "न्यूनतम" इंटरफ़ेस को अन्य सार्वजनिक फ़ंक्शन के सभी फ़ंक्शन से अलग करना चाहता था। एक उत्कृष्ट उदाहरण std :: C ++ में स्ट्रिंग होगा। इसमें 103 सदस्य कार्य हैं, लेकिन उनमें से केवल 32 को वास्तव में निजी सदस्यों तक पहुंच की आवश्यकता हैयदि आपके पास एक ऐसा वर्ग होता है, जो सभी "नॉन-कोर" कार्यों को "कोर एपीआई" के माध्यम से लगातार चलने के लिए मजबूर करता है, तो कार्यान्वयन को परीक्षण, डिबग और रिफ्लेक्टर के लिए बहुत आसान बना सकता है।


1
यदि आपके पास एक वर्ग है जो जटिल है, तो आपको इसे ठीक करने के लिए मजबूर किया जाना चाहिए, न कि बैंड-सहायता।
डेडज़ैम

माना। मुझे शायद केवल 20-30 कार्यों के साथ एक उदाहरण चुनना चाहिए था।
इक्ष्रेक

1
"103 फ़ंक्शन" एक लाल हेरिंग का एक सा है। इंटरफ़ेस जटिलता के संदर्भ में, अधिभार विधियों को एक बार गिना जाना चाहिए।
अवनर शहर-काश्तन

मैं पूरी तरह से असहमत हूं। अलग-अलग अधिभार में अलग-अलग शब्दार्थ और अलग-अलग इंटरफेस हो सकते हैं।
डेडएमजी

यहां तक ​​कि इस "छोटे" उदाहरण के लिए, getA() * getB()मध्यम और दीर्घकालिक में बेहतर है।
मार्क हर्ड
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.