paint-brush
जावा में अपरिवर्तनीय और विश्वसनीय वस्तुओं के साथ जीना सीखेंद्वारा@artemsutulov
621 रीडिंग
621 रीडिंग

जावा में अपरिवर्तनीय और विश्वसनीय वस्तुओं के साथ जीना सीखें

द्वारा Artem Sutulov1m2022/05/28
Read on Terminal Reader
Read this story w/o Javascript

बहुत लंबा; पढ़ने के लिए

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

Companies Mentioned

Mention Thumbnail
Mention Thumbnail
featured image - जावा में अपरिवर्तनीय और विश्वसनीय वस्तुओं के साथ जीना सीखें
Artem Sutulov HackerNoon profile picture

जटिल प्रोजेक्ट लिखते समय, एक अच्छी कोड संस्कृति विकसित करना आवश्यक है। अपरिवर्तनीय और सुसंगत वस्तुओं का उपयोग सबसे महत्वपूर्ण में से एक है।


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


अपने पिछलेलेख में, मैंने दिखाया है कि कैसे हम मानक वस्तुओं की स्थिरता और विश्वसनीयता में सुधार कर सकते हैं। कुछ ही शब्दों में:


  • मान सेट करते समय सत्यापन जोड़ें

  • प्रत्येक अशक्त क्षेत्र के लिए java.util.Optional का प्रयोग करें

  • जटिल उत्परिवर्तनों को एक उचित स्थान पर रखें - स्वयं जिम्मेदार वर्ग के लिए।


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

समस्या

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


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


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


  1. सेटर तरीके न बनाएं
  2. सभी फ़ील्ड को अंतिम बनाएं
  3. परिवर्तनशील वस्तुओं के साथ उदाहरण साझा न करें
  4. उपवर्गों को विधियों को ओवरराइड करने की अनुमति न दें (मैं इस लेख में इसे छोड़ दूंगा)


लेकिन उस तरह की वस्तु के साथ कैसे रहें? जब हमें इसे बदलने की आवश्यकता होती है, तो हमें एक प्रतिलिपि बनाने की आवश्यकता होती है; हम हर बार कोड और लॉजिक को कॉपी-पेस्ट किए बिना इसे अच्छी तरह से कैसे कर सकते हैं?

हमारे उदाहरण कक्षाओं के बारे में कुछ शब्द

मान लीजिए कि हमारे पास खाते हैं। प्रत्येक खाते में एक id , status और email होता है। खातों को ईमेल के माध्यम से सत्यापित किया जा सकता है। जब स्थिति CREATED जाती है, तो हम ईमेल के भरे जाने की अपेक्षा नहीं करते हैं। लेकिन जब यह VERIFIED या INACTIVE हो, तो ईमेल भरना होगा।



खाते की स्थिति


 public enum AccountStatus { CREATED, VERIFIED, INACTIVE }


कैनन Account.java कार्यान्वयन:


 public class Account { private final String id; private final AccountStatus status; private final String email; public Account(String id, AccountStatus status, String email) { this.id = id; this.status = status; this.email = email; } // equals / hashCode / getters }


आइए कल्पना करें कि हमने एक खाता बनाया है। फिर, कहीं न कहीं व्यावसायिक तर्क में, हमें एक ईमेल बदलने की आवश्यकता है।


 var account = new Account("some-id", CREATED, null);


हम वह कैसे कर सकते है? डिफ़ॉल्ट तरीका काम नहीं करेगा, हम एक अपरिवर्तनीय वर्ग के साथ बसने वाले नहीं हो सकते।


 account.setEmail("[email protected]");// we can not do that, we have no setters


ऐसा करने का एकमात्र तरीका कंस्ट्रक्टर के लिए एक नया उदाहरण और स्थान बनाना है जो पिछले मान हैं:


 var withEmail = new Account(account.getId(), CREATED, "[email protected]");


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

समाधान

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


ऑब्जेक्ट बनाने के लिए, मैं 'बिल्डर' पैटर्न का उपयोग करता हूं। यह बहुत प्रसिद्ध है, और आपके IDE के लिए इसे स्वचालित रूप से उत्पन्न करने के लिए बहुत सारे प्लगइन्स हैं।


 public class Account { private final String id; private final AccountStatus status; private final Optional<String> email; public Account(Builder builder) { this.id = notEmpty(builder.id); this.status = notNull(builder.status); this.email = checkEmail(builder.email); } public Account verify(String email) { return copy() .status(VERIFIED) .email(of(email)) .build(); } public Account changeEmail(String email) { return copy() .email(of(email)) .build(); } public Account deactivate() { return copy() .status(INACTIVE) .build(); } private Optional<String> checkEmail(Optional<String> email) { isTrue( notNull(email).map(StringUtils::isNotBlank).orElse(false) || this.status.equals(CREATED), "Email must be filled when status %s", this.status ); return email; } public static final class Builder { private String id; private AccountStatus status; private Optional<String> email = empty(); private Builder() { } public static Builder account() { return new Builder(); } public Builder id(String id) { this.id = id; return this; } public Builder status(AccountStatus status) { this.status = status; return this; } public Builder email(Optional<String> email) { this.email = email; return this; } public Account build() { return new Account(this); } } // equals / hashCode / getters }


जैसा कि आप देख सकते हैं, हमारी कक्षा में एक निजी विधि copy है जो Builder को एक सटीक प्रति के साथ लौटाती है। यह सभी क्षेत्रों की प्रतिलिपि-चिपकाने को हटा देता है, हालांकि, यह महत्वपूर्ण है कि यह विधि बाहरी Account.java से पहुंच योग्य नहीं है, क्योंकि उस बिल्डर के बाहर, हम राज्य और स्थिरता पर नियंत्रण खो देते हैं।

नए तरीके से खाते बदलना

अब, एक खाता बनाते हैं:


 var account = account() .id("example-id") .status(CREATED) .email((empty()) .build();


जब हमें एक ईमेल बदलने की आवश्यकता होती है, तो हमें एक प्रति बनाने के लिए जिम्मेदार होने की आवश्यकता नहीं होती है, हम केवल Account से ही एक विधि कहते हैं:


 var withNewEmail = account.changeEmail("[email protected]");


एक इकाई परीक्षण में इसका प्रदर्शन:


 @Test void should_successfully_change_email() { // given var account = account() .id("example-id") .status(VERIFIED) .email(of("[email protected]")) .build(); var newEmail = "[email protected]"; // when var withNewEmail = account.changeEmail(newEmail); // then assertThat(withNewEmail.getId()).isEqualTo(account.getId()); assertThat(withNewEmail.getStatus()).isEqualTo(account.getStatus()); assertThat(withNewEmail.getEmail()).isEqualTo(of(newEmail)); }


किसी खाते को सत्यापित करने के लिए, हम VERIFIED स्थिति और एक नए ईमेल के साथ एक प्रति नहीं बनाते हैं। हम केवल विधि को verify कहते हैं, जो न केवल हमारे लिए एक प्रति बनाएगी, बल्कि एक ईमेल की वैधता की भी जांच करेगी।


 @Test void should_successfully_verify_account() { // given var created = account() .id("example-id") .status(CREATED) .build(); var email = "[email protected]"; // when var verified = created.verify(email); // then assertThat(verified.getId()).isEqualTo(created.getId()); assertThat(verified.getStatus()).isEqualTo(VERIFIED); assertThat(verified.getEmail().get()).isEqualTo(email); }


निष्कर्ष

अपरिवर्तनीय, सुसंगत और विश्वसनीय वस्तुओं के साथ रहना कठिन है, लेकिन उचित तरीके से, यह बहुत आसान हो सकता है।


एक को लागू करते समय, को मत भूलना :

  1. सभी फ़ील्ड को अंतिम बनाएं
  2. सेटर्स प्रदान न करें
  3. परिवर्तनशील वस्तुओं के लिंक साझा न करें
  4. उपवर्गों को विधियों को ओवरराइड करने की अनुमति न दें
  5. अपनी कक्षा से उत्परिवर्तन विधियाँ प्रदान करें
  6. लागू करें निजी जिम्मेदार वर्ग के अंदर copy विधि जो एक Builder लौटाती है, और इसका उपयोग अपनी कक्षा के अंदर नए उदाहरण बनाने के लिए करती है।
  7. मूल्यों पर सत्यापन का उपयोग करके फ़ील्ड की निरंतरता बनाए रखें
  8. अशक्त क्षेत्रों के साथ Optional का प्रयोग करें


आप गिटहब पर अधिक यूनिट परीक्षणों के साथ पूरी तरह से काम कर रहे उदाहरण पा सकते हैं।


Unsplash . पर एडम नीसिओरुक द्वारा लीड फोटो