क्या मैं एनकैप्सुलेशन अति प्रयोग से पीड़ित हूं?


11

मैंने विभिन्न परियोजनाओं में अपने कोड में कुछ देखा है जो लगता है कि मेरे लिए कोड गंध और कुछ बुरा है, लेकिन मैं इससे निपट नहीं सकता।

"क्लीन कोड" लिखने की कोशिश करते समय मैं अपने कोड को पढ़ने में आसान बनाने के लिए निजी तरीकों का इस्तेमाल करता हूं। समस्या यह है कि कोड वास्तव में क्लीनर है, लेकिन यह परीक्षण करना भी अधिक कठिन है (हाँ, मुझे पता है कि मैं निजी तरीकों का परीक्षण कर सकता हूं ...) और सामान्य तौर पर यह मुझे बुरी आदत लगती है।

यहां एक वर्ग का एक उदाहरण है जो एक .csv फ़ाइल से कुछ डेटा पढ़ता है और ग्राहकों का एक समूह (विभिन्न क्षेत्रों और विशेषताओं के साथ दूसरी वस्तु) लौटाता है।

public class GroupOfCustomersImporter {
    //... Call fields ....
    public GroupOfCustomersImporter(String filePath) {
        this.filePath = filePath;
        customers = new HashSet<Customer>();
        createCSVReader();
        read();
        constructTTRP_Instance();
    }

    private void createCSVReader() {
        //....
    }

    private void read() {
        //.... Reades the file and initializes the class attributes
    }

    private void readFirstLine(String[] inputLine) {
        //.... Method used by the read() method
    }

    private void readSecondLine(String[] inputLine) {
        //.... Method used by the read() method
    }

    private void readCustomerLine(String[] inputLine) { 
        //.... Method used by the read() method
    }

    private void constructGroupOfCustomers() {
        //this.groupOfCustomers = new GroupOfCustomers(**attributes of the class**);
    }

    public GroupOfCustomers getConstructedGroupOfCustomers() {
        return this.GroupOfCustomers;
    }
}

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

GroupOfCustomersImporter importer = new GroupOfCustomersImporter(filepath)
importer.createCSVReader();
read();
GroupOfCustomer group = constructGoupOfCustomerInstance();

मैं इसे पसंद करता हूं क्योंकि मैं क्लाइंट के साइड कोड को कार्यान्वयन विवरण के साथ परेशान करने वाले कोड के बेकार लाइनों को नहीं डालना चाहता हूं।

तो, क्या यह वास्तव में एक बुरी आदत है? यदि हाँ, तो मैं इससे कैसे बच सकता हूँ? कृपया ध्यान दें कि उपरोक्त केवल एक सरल उदाहरण है। उसी स्थिति की कल्पना करें जो किसी चीज़ में थोड़ी अधिक जटिल है।

जवाबों:


17

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

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

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

हालाँकि, यदि आप ऐसा करते हैं और आपको अभी भी लगता है कि आप आसानी से कक्षा के प्रत्येक भाग का परीक्षण नहीं कर सकते हैं, तो यह बहुत संभव है कि आपकी एक कक्षा बहुत अधिक कर रही हो। SRP सिद्धांत की भावना में , आप अनुसरण कर सकते हैं कि माइकल फेदर "स्प्राउट क्लास पैटर्न" को क्या कहते हैं और अपने मूल वर्ग का एक हिस्सा नए वर्ग में निकालते हैं।

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


1

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

GroupOfCustomersImporter importer = 
    new GroupOfCustomersImporter.CreateInstance();

या शायद एक अलग कारखाने वर्ग की एक विधि के रूप में:

ImporterFactory factory = new ImporterFactory();
GroupOfCustomersImporter importer = factory.CreateGroupOfCustomersImporter();

उन विकल्पों में से सिर्फ पहले दो जोड़े हैं जो सीधे मेरे सिर के ऊपर से निकलते हैं।

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


1

मेरा अपना जवाब जोड़ना, क्योंकि यह एक टिप्पणी के लिए बहुत लंबा हो गया।

आप पूरी तरह से सही हैं कि एनकैप्सुलेशन और परीक्षण बहुत विपरीत हैं - यदि आप लगभग सब कुछ छिपाते हैं, तो परीक्षण करना कठिन है।

हालाँकि, आप फ़ाइल नाम के बजाय एक स्ट्रीम देकर उदाहरण को और अधिक परीक्षण योग्य बना सकते हैं - इस तरह आप केवल मेमोरी से एक ज्ञात सीएसवी, या यदि आप चाहें, तो एक फ़ाइल की आपूर्ति कर सकते हैं। जब आपको http समर्थन जोड़ने की आवश्यकता होगी, तो यह आपकी कक्षा को और अधिक मजबूत बना देगा;)

इसके अलावा, आलसी-इनिट का उपयोग करने पर ध्यान दें:

public class Csv
{
  private CustomerList list;

  public CustomerList get()
  {
    if(list == null)
        load();
     return list;
  }
}

1

कंस्ट्रक्टर को कुछ वैरिएबल या ऑब्जेक्ट के स्टेट को इनिशियलाइज़ करने के अलावा बहुत ज्यादा नहीं करना चाहिए। कंस्ट्रक्टर में बहुत अधिक करने से रनटाइम अपवाद बढ़ सकते हैं। मैं इस तरह somenthing कर ग्राहक से बचने की कोशिश करेंगे:

MyClass a;
try {
   a = new MyClass();
} catch (MyException e) {
   //do something
}

करने के बजाय:

MyClass a = new MyClass(); // Or a factory method
try {
   a.doSomething();
} catch (MyException e) {
   //do something
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.