बिल्डर पैटर्न लागू करते समय हमें बिल्डर वर्ग की आवश्यकता क्यों है?


31

मैंने बिल्डर पैटर्न (मुख्य रूप से जावा में) के कई कार्यान्वयन देखे हैं। उन सभी में एक इकाई वर्ग (चलो एक Personवर्ग कहते हैं ), और एक बिल्डर वर्ग है PersonBuilder। बिल्डर विभिन्न प्रकार के फ़ील्ड "स्टैक" करता है और new Personपारित किए गए तर्कों के साथ वापस लौटता है । हमें सभी बिल्डर तरीकों को Personकक्षा में रखने के बजाय स्पष्ट रूप से एक बिल्डर वर्ग की आवश्यकता क्यों है?

उदाहरण के लिए:

class Person {

  private String name;
  private Integer age;

  public Person() {
  }

  Person withName(String name) {
    this.name = name;
    return this;
  }

  Person withAge(int age) {
    this.age = age;
    return this;
  }
}

मैं बस कह सकता हूं Person john = new Person().withName("John");

PersonBuilderवर्ग की आवश्यकता क्यों है ?

एकमात्र लाभ जो मुझे दिखाई देता है, क्या हम Personखेतों को finalइस प्रकार घोषित कर सकते हैं , इस प्रकार अपरिवर्तनीयता सुनिश्चित करते हैं।


4
नोट: इस पैटर्न का सामान्य नाम है chainable setters: D
Mooing Duck

4
एकल जिम्मेदारी सिद्धांत। यदि आप व्यक्ति वर्ग में तर्क रखते हैं, तो इसके लिए एक से अधिक काम करना होगा
reggaeguitar

1
आपका लाभ किसी भी तरह से हो सकता है: मैं withNameकेवल नाम फ़ील्ड में परिवर्तन के साथ व्यक्ति की एक प्रति वापस कर सकता हूं । दूसरे शब्दों में, Person john = new Person().withName("John");काम कर सकता है भले ही Personअपरिवर्तनीय है (और यह कार्यात्मक प्रोग्रामिंग में एक सामान्य पैटर्न है)।
ब्रायन मैककिनटन

9
@MooDDuck: इसके लिए एक और सामान्य शब्द एक धाराप्रवाह इंटरफ़ेस है
मैक

2
@ मुझे लगता है कि धाराप्रवाह इंटरफ़ेस थोड़ा अधिक सामान्य है - आप voidतरीकों से बचें । इसलिए, उदाहरण के लिए यदि Personउनके नाम पर एक विधि है जो उनके नाम को प्रिंट करता है, तो आप अभी भी इसे धाराप्रवाह इंटरफ़ेस के साथ जोड़ सकते हैं person.setName("Alice").sayName().setName("Bob").sayName()। वैसे, मैं अपने सुझाव के साथ JavaDoc में उन लोगों की व्याख्या करता हूं @return Fluent interface- यह सामान्य और स्पष्ट है जब यह किसी भी विधि पर लागू होता है return thisजो इसके निष्पादन के अंत में होता है और यह काफी स्पष्ट है। तो, एक बिल्डर एक धाराप्रवाह इंटरफ़ेस भी करेगा।
वीएलएजी

जवाबों:


27

ऐसा है कि आप अपरिवर्तनीय हो सकते हैं और एक ही समय में नामित मापदंडों का अनुकरण कर सकते हैं ।

Person p = personBuilder
    .name("Arthur Dent")
    .age(42)
    .build()
;

जब तक यह सेट नहीं हो जाता है, तब तक यह आपके व्यक्ति को बंद कर देता है और, एक बार सेट होने पर, आपको इसे बदलने नहीं देगा, फिर भी हर क्षेत्र को स्पष्ट रूप से लेबल किया गया है। आप जावा में सिर्फ एक वर्ग के साथ ऐसा नहीं कर सकते।

ऐसा लगता है कि आप जोश ब्लॉक्स बिल्डर पैटर्न के बारे में बात कर रहे हैं । यह गैंग ऑफ फोर बिल्डर पैटर्न से भ्रमित नहीं होना चाहिए । ये अलग-अलग जानवर हैं। वे दोनों निर्माण समस्याओं को हल करते हैं, लेकिन काफी अलग तरीके से।

बेशक आप किसी अन्य वर्ग का उपयोग किए बिना अपनी वस्तु का निर्माण कर सकते हैं। लेकिन फिर आपको चुनना होगा। आप या तो उन भाषाओं में नामित मापदंडों को अनुकरण करने की क्षमता खो देते हैं जो उनके पास नहीं हैं (जैसे जावा) या आप जीवन भर वस्तुओं के अपरिवर्तनीय रहने की क्षमता खो देते हैं।

अपरिवर्तनीय उदाहरण, मापदंडों के लिए कोई नाम नहीं है

Person p = new Person("Arthur Dent", 42);

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

पारंपरिक वासियों के साथ नामित नामित पैरामीटर उदाहरण। अपरिवर्तनीय नहीं है।

Person p = new Person();
p.name("Arthur Dent");
p.age(42);

यहां आप बसने वालों के साथ सब कुछ बना रहे हैं और नामित मापदंडों का अनुकरण कर रहे हैं, लेकिन अब आप अपरिवर्तनीय नहीं हैं। सेटर के प्रत्येक उपयोग से वस्तु स्थिति बदल जाती है।

इसलिए कक्षा को जोड़कर जो आप प्राप्त करते हैं वह आप दोनों कर सकते हैं।

build()यदि किसी लापता आयु क्षेत्र के लिए एक रनटाइम त्रुटि आपके लिए पर्याप्त है, तो सत्यापन किया जा सकता है । आप इसे अपग्रेड कर सकते हैं और लागू कर सकते हैं age()जिसे कंपाइलर त्रुटि के साथ कहा जाता है। सिर्फ जोश बलोच बिल्डर पैटर्न के साथ नहीं।

उसके लिए आपको एक आंतरिक डोमेन विशिष्ट भाषा (iDSL) की आवश्यकता है।

इससे आप मांग कर सकते हैं कि वे कॉल करें age()और कॉल करने name()से पहले build()। लेकिन आप इसे thisहर बार लौटाकर नहीं कर सकते । प्रत्येक चीज जो रिटर्न करती है वह एक अलग चीज है जो आपको अगली चीज को कॉल करने के लिए मजबूर करती है।

उपयोग इस तरह लग सकता है:

Person p = personBuilder
    .name("Arthur Dent")
    .age(42)
    .build()
;

लेकिन यह:

Person p = personBuilder
    .age(42)
    .build()
;

एक कंपाइलर त्रुटि का कारण बनता है क्योंकि age()केवल उसी प्रकार पर कॉल करने के लिए मान्य है जो उसके द्वारा लौटाया गया है name()

ये iDSL बेहद शक्तिशाली हैं ( उदाहरण के लिए JOOQ या Java8 स्ट्रीम ) और उपयोग करने के लिए बहुत अच्छे हैं, खासकर यदि आप कोड पूरा होने के साथ एक आईडीई का उपयोग करते हैं, लेकिन वे सेट करने के लिए एक उचित काम हैं। मैं उन्हें उन चीज़ों के लिए सहेजने की सलाह दूंगा जिनके पास उनके खिलाफ लिखे गए स्रोत कोड का एक सा होगा।


3
और फिर कोई पूछता है तुम क्यों है नाम प्रदान करने से पहले उम्र, और केवल उत्तर "क्योंकि मिश्रित विस्फोट" है। मुझे पूरा यकीन है कि कोई भी उस स्रोत से बच सकता है यदि किसी के पास C ++ जैसे उचित टेम्पलेट हैं, या हर चीज के लिए एक अलग प्रकार का उपयोग करके वापस चला जाता है।
Deduplicator

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

कक्षाओं का एक लाभ जो उनके किसी भी निर्माण नियम को नहीं जानता है, वह यह है कि जब वे नियम बदलते हैं तो वे परवाह नहीं करते हैं। मैं सिर्फ यह जोड़ सकता हूं AnonymousPersonBuilderकि इसमें नियमों का एक सेट है।
कैंडिड_ऑरेंज

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

1
@radarbob अभी भी बनाया जा रहा वर्ग जानता है कि कैसे खुद का निर्माण करना है। इसका कंस्ट्रक्टर ऑब्जेक्ट की अखंडता सुनिश्चित करने के लिए जिम्मेदार है। बिल्डर क्लास, कंस्ट्रक्टर के लिए केवल एक सहायक है, क्योंकि कंस्ट्रक्टर अक्सर बड़ी संख्या में पैरामीटर लेता है, जो एक बहुत अच्छा एपीआई के लिए नहीं बनाता है।
कोई यू

57

बिल्डर क्लास का उपयोग / प्रदान क्यों करें:

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

4
आखिरी गोली बिंदु के बारे में। इसलिए यह विचार कि PersonBuilderकोई गेटर्स नहीं है, और वर्तमान मानों का निरीक्षण करने का एकमात्र तरीका .Build()ए रिटर्न करने के लिए कॉल करना है Person। इस तरह से। बच्चों को सही ढंग से बनाई गई वस्तु को मान्य कर सकते हैं, सही? यानी यह "निर्माणाधीन" वस्तु के उपयोग को रोकने के लिए बनाया गया तंत्र है?
एरोनल्स

@AaronLS, हाँ, निश्चित रूप से; जटिल सत्यापन Buildको खराब और / या निर्माणाधीन वस्तुओं को रोकने के लिए एक ऑपरेशन में रखा जा सकता है ।
एरिक Eidt

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

2
@AaronLS एक बिल्डर को मिल सकता है; ये मुद्दा नहीं है। इसकी जरूरत नहीं है, लेकिन अगर किसी बिल्डर के पास गेटर्स हैं, तो आपको इस बारे में पता होना चाहिए कि बिल्डर के पास कॉलिंग तब getAgeवापस आ सकती है nullजब यह संपत्ति अभी तक निर्दिष्ट नहीं की गई है। इसके विपरीत, Personवर्ग अयोग्य को लागू कर सकता है nullजो कि उम्र कभी नहीं हो सकती है , जो कि असंभव है जब बिल्डर और वास्तविक वस्तु को मिलाया जाता है, जैसे ओपी के new Person() .withName("John")उदाहरण के साथ, जो खुशी से Personबिना उम्र के बनाता है । यह उत्परिवर्तनीय वर्गों पर भी लागू होता है, क्योंकि सेटर अपरिवर्तनीय लागू कर सकते हैं, लेकिन प्रारंभिक मूल्यों को लागू नहीं कर सकते हैं।
होलकर

1
@ अपने अंक सही हैं, लेकिन मुख्य रूप से इस तर्क का समर्थन करते हैं कि एक बिल्डर को गेटर्स से बचना चाहिए।
user949300

21

एक कारण यह सुनिश्चित करना होगा कि पारित आंकड़ों के सभी व्यावसायिक नियमों का पालन करते हैं।

आपका उदाहरण इस पर ध्यान नहीं देता है, लेकिन मान लीजिए कि कोई खाली स्ट्रिंग या विशेष वर्णों से युक्त स्ट्रिंग में पास हुआ है। आप यह सुनिश्चित करने के आसपास किसी प्रकार का तर्क करना चाहेंगे कि उनका नाम वास्तव में एक वैध नाम है (जो वास्तव में एक बहुत कठिन काम है)।

आप अपने व्यक्तिगत वर्ग में यह सब डाल सकते हैं, खासकर यदि तर्क बहुत छोटा है (उदाहरण के लिए, बस यह सुनिश्चित करना कि एक उम्र गैर-नकारात्मक है) लेकिन जैसे-जैसे तर्क बढ़ता है, यह इसे अलग करने के लिए समझ में आता है।


7
प्रश्न में उदाहरण एक सरल नियम उल्लंघन प्रदान करता है: कोई उम्र नहीं दी गई हैjohn
Caleth

6
+1 और बिल्डर वर्ग के बिना एक साथ दो क्षेत्रों के लिए नियम बनाना बहुत कठिन है (फ़ील्ड X + Y 10 से अधिक नहीं हो सकता है)।
रेगिनाल ब्लू

7
@ReginaldBlue यह और भी कठिन है अगर वे "x + y सम है" से बंधे हैं। हमारी प्रारंभिक स्थिति (0,0) के साथ ठीक है, राज्य (1,1) भी ठीक है, लेकिन एकल चर अपडेट का कोई क्रम नहीं है, जो दोनों राज्य को वैध रखता है और हमें (0,0) से (1) , 1)।
माइकल एंडरसन

मेरा मानना ​​है कि यह सबसे बड़ा फायदा है। बाकी सब अच्छे-अच्छे हैं।
जियाको अल्जेटा

5

इस पर थोड़ा अलग कोण जो मैं अन्य उत्तरों में देखता हूं।

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

class Person {
  private final String name;
  private final Integer age;

  private Person(String name, String age) {
    this.name = name;
    this.age = age;
  }  

  public Person() {
    this.name = null;
    this.age = null;
  }

  Person withName(String name) {
    return new Person(name, this.age);
  }

  Person withAge(int age) {
    return new Person(this.name, age);
  }
}

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

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


2

एक अन्य कारण जो स्पष्ट रूप से यहां पहले से ही उल्लेख नहीं किया गया है, वह यह है कि build()विधि यह सत्यापित कर सकती है कि सभी फ़ील्ड 'मान्य मान हैं (या तो सीधे सेट किए गए हैं, या अन्य फ़ील्ड के अन्य मानों से व्युत्पन्न हैं), जो संभवतः सबसे संभावित विफलता मोड है। अन्यथा ऐसा होगा।

एक और लाभ यह है कि आपकी Personवस्तु अधिक सरल जीवनकाल और अनियंत्रित का एक सरल सेट समाप्त हो जाएगी। आप जानते हैं कि आपके पास एक बार Person p, आपके पास एक p.nameऔर एक मान्य है p.age। आपके किसी भी तरीके को "अच्छी तरह से अगर उम्र निर्धारित की गई है लेकिन नाम नहीं, या क्या नाम है लेकिन उम्र निर्धारित नहीं है" जैसी स्थितियों को संभालने के लिए डिज़ाइन किया जाना है? यह कक्षा की समग्र जटिलता को कम करता है।


"[...] सत्यापित कर सकता है कि सभी फ़ील्ड सेट हैं [...]" बिल्डर पैटर्न की बात यह है कि आपको सभी फ़ील्ड स्वयं सेट करने की आवश्यकता नहीं है और आप समझदार चूक में वापस आ सकते हैं। यदि आपको वैसे भी सभी फ़ील्ड सेट करने की आवश्यकता है, तो शायद आपको उस पैटर्न का उपयोग नहीं करना चाहिए!
विंसेंट सवार्ड

@VincentSavard वास्तव में, लेकिन मेरे अनुमान में, यह सबसे आम उपयोग है। यह मानने के लिए कि मूल्यों के कुछ "कोर" सेट हैं, और कुछ वैकल्पिक लोगों के आसपास कुछ फैंसी उपचार करते हैं (चूक को स्थापित करना, अन्य मूल्यों से उनका मूल्य प्राप्त करना, आदि)।
अलेक्जेंडर - मोनिका

यह कहने के बजाय कि 'सत्यापित करें कि सभी फ़ील्ड सेट हैं', मैं इसे 'सत्यापित करने के लिए बदल दूँगा कि सभी फ़ील्ड में मान्य मान हैं (कभी-कभी अन्य फ़ील्ड के मूल्यों पर निर्भर]' (अर्थात) केवल मान के आधार पर कोई फ़ील्ड कुछ मानों की अनुमति दे सकती है। दूसरे क्षेत्र के)। यह प्रत्येक सेटर में किया जा सकता है, लेकिन किसी के लिए वस्तु को पूरा किए बिना अपवाद को स्थापित करना आसान होता है जब तक कि ऑब्जेक्ट पूरा न हो जाए और निर्मित होने के लिए तैयार हो।
यित्ज़िह

निर्माण किए जा रहे वर्ग का निर्माता अंततः अपने तर्कों की वैधता के लिए जिम्मेदार है। बिल्डर में मान्यता सबसे बेमानी है, और आसानी से निर्माणकर्ता की आवश्यकताओं के साथ सिंक से बाहर निकल सकता है।
कोई यू

@TKK वास्तव में। लेकिन वे विभिन्न चीजों को मान्य करते हैं। कई मामलों में मैंने बिल्डर का उपयोग किया है, बिल्डर का काम उन इनपुटों का निर्माण करना था जो अंततः निर्माता को दिए जाते हैं। उदाहरण के लिए, बिल्डर को ए URI, ए File, या ए के साथ कॉन्फ़िगर किया FileInputStreamगया है और जो कुछ भी उसे प्रदान करने के लिए प्रदान किया गया था, FileInputStreamवह अंततः एक आर्ग के रूप में कंस्ट्रक्टर कॉल में जाता है
अलेक्जेंडर - मोनिका

2

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


2

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

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

इस प्रकार बिल्डर क्लास (अर्थात व्यक्ति वर्ग के अलावा कुछ बाहरी संस्था) का उपयोग करके हम यह सुनिश्चित करते हैं कि ऑब्जेक्ट असंगत स्थिति में कभी नहीं होगा।


@ दाविदो आपको मेरी बात सही लगी। मुख्य जोर मैं यहाँ लगाने की कोशिश कर रहा हूँ जो असंगत स्थिति पर है। और यह बिल्डर पैटर्न का उपयोग करने का एक मुख्य लाभ है।
शुभम बॉन्डार्डे

2

बिल्डर ऑब्जेक्ट का पुनः उपयोग करें

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

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


1

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

यह केवल तभी काम करता है जब एक प्रारंभिक (वैध / उपयोगी) ऑब्जेक्ट प्राप्त करने का एक तरीका होता है (जैसे एक निर्माता से जो सभी आवश्यक फ़ील्ड सेट करता है, या एक फ़ैक्टरी विधि जो डिफ़ॉल्ट मान सेट करता है), और अतिरिक्त बिल्डर विधियाँ फिर संशोधित वस्तुओं को आधारित करती हैं मौजूदा एक पर। उन बिल्डर तरीकों को यह सुनिश्चित करने की आवश्यकता है कि आपको रास्ते में अवैध / असंगत वस्तुएं नहीं मिलती हैं।

बेशक, इसका मतलब है कि आपके पास बहुत सी नई वस्तुएं होंगी, और अगर आपकी वस्तुएं बनाने के लिए महंगी हैं, तो आपको ऐसा नहीं करना चाहिए।

मैंने अपने व्यावसायिक वस्तुओं के लिए हैमरेस्ट मैचर्स बनाने के लिए टेस्ट कोड का उपयोग किया । मुझे सटीक कोड याद नहीं है, लेकिन यह कुछ ऐसा दिखता है (सरलीकृत):

public class CustomerMatcher extends TypeSafeMatcher<Customer> {
    private final Matcher<? super String> nameMatcher;
    private final Matcher<? super LocalDate> birthdayMatcher;

    @Override
    protected boolean matchesSafely(Customer c) {
        return nameMatcher.matches(c.getName()) &&
               birthdayMatcher.matches(c.getBirthday());
    }

    private CustomerMatcher(Matcher<? super String> nameMatcher,
                            Matcher<? super LocalDate> birthdayMatcher) {
        this.nameMatcher = nameMatcher;
        this.birthdayMatcher = birthdayMatcher;
    }

    // builder methods from here on

    public static CustomerMatcher isCustomer() {
        // I could return a static instance here instead
        return new CustomerMatcher(Matchers.anything(), Matchers.anything());
    }

    public CustomerMatcher withBirthday(Matcher<? super LocalDate> birthdayMatcher) {
        return new CustomerMatcher(this.nameMatcher, birthdayMatcher);
    }

    public CustomerMatcher withName(Matcher<? super String> nameMatcher) {
        return new CustomerMatcher(nameMatcher, this.birthdayMatcher);
    }
}

मैं तब अपने यूनिट परीक्षणों (उपयुक्त स्थैतिक आयातों के साथ) में इसका उपयोग करूंगा:

assertThat(result, is(customer().withName(startsWith("Paŭlo"))));

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

@BillK सच - एक वस्तु, अनिवार्य रूप से, अपरिवर्तनीय बसने वालों (जो हर बार एक नई वस्तु लौटाती है) का अर्थ है कि लौटी प्रत्येक वस्तु वैध होनी चाहिए। यदि आप अमान्य वस्तुएं लौटा रहे हैं (उदाहरण के लिए, व्यक्ति)name लेकिन नहीं केage ) , तो अवधारणा काम नहीं करती है। ठीक है, आप वास्तव में कुछ वापस कर सकते हैं जैसे कि PartiallyBuiltPersonयह वैध नहीं है लेकिन यह बिल्डर को मुखौटा बनाने के लिए एक हैक की तरह लगता है।
वीएलएज़ी

@BillK का मेरा मतलब है "अगर एक प्रारंभिक (वैध / उपयोगी) वस्तु प्राप्त करने का एक तरीका है" - मेरा मानना ​​है कि प्रत्येक बिल्डर फ़ंक्शन से लौटी प्रारंभिक वस्तु और वस्तु दोनों ही मान्य / सुसंगत हैं। इस तरह के एक वर्ग के निर्माता के रूप में आपका काम केवल बिल्डर तरीकों को प्रदान करना है जो उन लगातार परिवर्तन करते हैं।
पाओलो एबरमन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.