क्या सेटर रिटर्न "यह" बनाने के लिए बुरा व्यवहार है?


249

क्या यह "यह" जावा रिटर्न में बसने के लिए एक अच्छा या बुरा विचार है?

public Employee setName(String name){
   this.name = name;
   return this;
}

यह पैटर्न उपयोगी हो सकता है क्योंकि तब आप इस तरह से बसने की श्रृंखला बना सकते हैं:

list.add(new Employee().setName("Jack Sparrow").setId(1).setFoo("bacon!"));

इसके अलावा:

Employee e = new Employee();
e.setName("Jack Sparrow");
...and so on...
list.add(e);

... लेकिन यह मानक सम्मेलन के खिलाफ जाता है। मुझे लगता है कि यह सिर्फ इसलिए सार्थक हो सकता है क्योंकि यह उस सेटर को कुछ और उपयोगी बना सकता है। मैंने देखा है कि यह पैटर्न कुछ स्थानों (जैसे JMock, JPA) का उपयोग करता है, लेकिन यह असामान्य लगता है, और केवल आम तौर पर बहुत अच्छी तरह से परिभाषित एपीआई के लिए उपयोग किया जाता है जहां यह पैटर्न हर जगह उपयोग किया जाता है।

अपडेट करें:

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


1
चूंकि मैंने इस डिज़ाइन पैटर्न को कुछ समय पहले देखा है, इसलिए मैं जहाँ भी संभव हो ऐसा करता हूँ। यदि किसी विधि को स्पष्ट रूप से अपना काम करने के लिए कुछ वापस करने की आवश्यकता नहीं है, तो यह अब लौटता है this। कभी-कभी, मैं फ़ंक्शन को बदल भी देता हूं ताकि मूल्य वापस करने के बजाय, यह ऑब्जेक्ट के सदस्य पर संचालित हो, बस इसलिए मैं यह कर सकता हूं। यह अद्भुत है। :)
Inversus

4
टेलिस्कोपिक सेटलर्स के लिए सेल्फ लौटना और बिल्डरों में मैं सेटनाम (स्ट्रिंग नाम) के बजाय नाम (स्ट्रिंग नाम) का उपयोग करना पसंद करता हूं। जैसा कि आपने बताया कि सेटर के लिए एक सामान्य अभ्यास और अपेक्षा शून्य है। "गैर-मानक"
बसने वाले

कृपया प्रत्येक आह्वान से पहले लाइन ब्रेक का परिचय दें :) और अपने आईडीई को कॉन्फ़िगर करें या एक उचित एक प्राप्त करें यदि यह इसका सम्मान नहीं करता है।
MauganRa

व्यापक रूप से उपयोग किए जाने वाले चौखटे (उदाहरण के लिए स्प्रिंग और हाइबरनेट) सख्ती से (कम से कम वे उपयोग करते थे) शून्य-
बस्तियों के

जवाबों:


83

मुझे नहीं लगता कि इसमें कुछ विशेष रूप से गलत है, यह सिर्फ शैली की बात है। यह उपयोगी है जब:

  • आपको एक साथ कई फ़ील्ड सेट करने होंगे (निर्माण पर भी)
  • आप जानते हैं कि आपको उस समय कौन से फ़ील्ड सेट करने की आवश्यकता है जो आप कोड लिख रहे हैं, और
  • कई अलग-अलग संयोजन हैं जिनके लिए आप फ़ील्ड सेट करना चाहते हैं।

इस विधि के विकल्प हो सकते हैं:

  1. एक मेगा कंस्ट्रक्टर (नकारात्मक पक्ष: आप बहुत सारे नल या डिफ़ॉल्ट मान पारित कर सकते हैं, और यह जानना कठिन हो जाता है कि कौन सा मान मेरे लिए संगत है)
  2. कई ओवरलोडेड कंस्ट्रक्टर (नकारात्मक पक्ष: एक बार जब आप कुछ से अधिक होते हैं, तो वह बेकार हो जाता है)
  3. फैक्ट्री / स्टैटिक मेथड (डाउनसाइड: ओवरलोडेड कंस्ट्रक्टर्स के समान - एक बार कुछ से अधिक होने पर अनिच्छुक हो जाता है)

यदि आप केवल एक बार में कुछ गुण सेट करने जा रहे हैं, तो मैं कहूंगा कि यह 'इस' लौटने के लायक नहीं है। यह निश्चित रूप से नीचे गिर जाता है यदि आप बाद में स्थिति / सफलता सूचक / संदेश की तरह कुछ और वापस करने का निर्णय लेते हैं।


1
ठीक है, आम तौर पर आप किसी भी तरह से एक सेटर से, कन्वेंशन से वापस नहीं आते हैं।
केन लियू

17
शायद के साथ शुरू करने के लिए नहीं, लेकिन एक सेटर जरूरी अपने मूल उद्देश्य को नहीं रखता है। एक वैरिएबल के रूप में उपयोग की जाने वाली स्थिति एक ऐसी स्थिति में बदल सकती है जिसमें कई चर शामिल होते हैं या अन्य दुष्प्रभाव होते हैं। कुछ सेटर पिछले मूल्य को वापस कर सकते हैं, अन्य विफलता सूचक लौटा सकते हैं यदि विफलता अपवाद के लिए बहुत सामान्य है। हालांकि एक और दिलचस्प बात यह उठती है: क्या होगा यदि आपके द्वारा उपयोग किया जाने वाला टूल / ढांचा आपके सेटर को पहचानता नहीं है, जब उनके पास रिटर्न मान है?
टॉम क्लिफ्ट

12
@ अच्छा बिंदु, ऐसा करने से गेटर्स और बसने वालों के लिए "जावा बीन" सम्मेलन टूट जाता है।
एंडी व्हाइट

2
@TomClift "जावा बीन" सम्मेलन को तोड़ने से क्या कोई समस्या होती है? लाइब्रेरी जो "जावा बीन" कन्वेंशन का उपयोग रिटर्न प्रकार या सिर्फ विधि मापदंडों और विधि के नाम को देखते हैं।
थियो ब्रिस्को डे

3
यही कारण है कि बिल्डर पैटर्न मौजूद है, बसने वालों को कुछ वापस नहीं करना चाहिए, इसके बजाय, एक बिल्डर बनाएं अगर इसकी ज़रूरत बेहतर लगती है और कम कोड लेता है :)
रिकार्डो

106

यह बुरा अभ्यास नहीं है। यह एक आम प्रचलन है। यदि आप ऐसा नहीं करना चाहते हैं तो अधिकांश भाषाओं को आपको लौटी हुई वस्तु से निपटने की आवश्यकता नहीं है क्योंकि यह "सामान्य" सेटर उपयोग सिंटैक्स को नहीं बदलता है, लेकिन आपको एक साथ चेन सेटल करने की अनुमति देता है।

इसे आमतौर पर एक बिल्डर पैटर्न या एक धाराप्रवाह इंटरफ़ेस कहा जाता है ।

यह जावा एपीआई में भी आम है:

String s = new StringBuilder().append("testing ").append(1)
  .append(" 2 ").append(3).toString();

26
यह अक्सर बिल्डरों में उपयोग किया जाता है, लेकिन मैं यह नहीं कहूंगा कि "यह एक बिल्डर पैटर्न है"।
लॉरेंस गोन्साल्व्स

10
यह मेरे लिए मजाकिया है कि धाराप्रवाह इंटरफेस के लिए कुछ तर्क यह है कि वे कोड को पढ़ना आसान बनाते हैं। मैं इसे लिखने के लिए अधिक सुविधाजनक देख सकता था, लेकिन यह मुझे पढ़ने के लिए कठिन होने के रूप में मारता है। मेरे पास केवल यही असहमति है।
ब्रेंट राइट्स कोड

30
इसे ट्रेन-मलबे एंटीपैटर्न के रूप में भी जाना जाता है। समस्या यह है कि जब एक नल-पॉइंटर अपवाद स्टैक ट्रेस में इस तरह की एक पंक्ति होती है, तो आपको पता नहीं है कि आह्वान ने अशक्त लौटा दिया है। यह कहना नहीं है कि हर कीमत पर जंजीरों से बचा जाना चाहिए, लेकिन खराब पुस्तकालयों से सावधान रहना चाहिए।
ddimitrov

18
@ddimitrov आप इसे सीमित कर के रूप में aslong लौटने के लिए इस यह एक समस्या (केवल पहली invokation एनपीई फेंक सकता है) हो कभी नहीं होगा
स्टीफन

4
यह लिखना आसान है और यह मानकर पढ़ना है कि आपने लाइनब्रीक और इंडेशन को रखा है, जहां पठनीयता अन्यथा पीड़ित होगी! (क्योंकि यह दोहराए जाने वाले कोड के निरर्थक अव्यवस्था से बचा जाता है जैसे bla.foo.setBar1(...) ; bla.foo.setBar2(...)कि आप कब लिख bla.foo /* newline indented */.setBar1(...) /* underneath previous setter */ .setBar2(...)सकते थे (एसओ टिप्पणी में लाइनब्रेक का इस तरह उपयोग नहीं कर सकते :-( ... आशा है कि आप 10 ऐसे बस या अधिक जटिल कॉलों पर विचार कर सकते हैं)
एंड्रियास डिट्रिच

90

संक्षेप में:

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

कुछ अन्य बिंदुओं का उल्लेख नहीं किया गया है:

  • यह प्रिंसिपल का उल्लंघन करता है कि प्रत्येक फ़ंक्शन को एक (और केवल एक) काम करना चाहिए। आप इस पर विश्वास कर सकते हैं या नहीं, लेकिन जावा में मेरा मानना ​​है कि यह अच्छी तरह से काम करता है।

  • आईडीई आपके लिए (डिफ़ॉल्ट रूप से) यह उत्पन्न नहीं करने वाले हैं।

  • मैं अंत में, यहाँ एक वास्तविक दुनिया डेटा बिंदु है। मुझे इस तरह से निर्मित एक पुस्तकालय का उपयोग करने में समस्याएं हुई हैं। हाइबरनेट का क्वेरी बिल्डर मौजूदा लाइब्रेरी में इसका एक उदाहरण है। चूंकि क्वेरी के सेट * तरीके क्वेरीज़ लौटा रहे हैं, इसलिए केवल हस्ताक्षर को देखकर यह बताना असंभव है कि इसका उपयोग कैसे किया जाए। उदाहरण के लिए:

    Query setWhatever(String what);
  • यह एक अस्पष्टता का परिचय देता है: क्या विधि वर्तमान वस्तु (आपके पैटर्न) को संशोधित करती है या, शायद क्वेरी वास्तव में अपरिवर्तनीय (एक बहुत लोकप्रिय और मूल्यवान पैटर्न) है, और विधि एक नया लौटा रही है। यह सिर्फ पुस्तकालय का उपयोग करने के लिए कठिन बनाता है, और कई प्रोग्रामर इस सुविधा का फायदा नहीं उठाते हैं। यदि बसने वाले बस गए थे, तो यह स्पष्ट होगा कि इसका उपयोग कैसे किया जाए।


5
btw, यह "धाराप्रवाह" है, न कि "तरल पदार्थ" ... जैसा कि इसमें आप एक बोली जाने वाली भाषा की तरह विधि कॉल की एक श्रृंखला की संरचना करते हैं।
केन लिउ

1
अपरिवर्तनीयता के बारे में अंतिम बिंदु बहुत महत्वपूर्ण है। सबसे सरल उदाहरण स्ट्रिंग है। जावा देवों को उम्मीद है कि स्ट्रिंग पर विधियों का उपयोग करते समय उन्हें पूरी तरह से नया उदाहरण मिलता है और वही नहीं बल्कि संशोधित उदाहरण। धाराप्रवाह इंटरफ़ेस के साथ विधि प्रलेखन में यह उल्लेख करना होगा कि लौटी हुई वस्तु नए उदाहरण के बजाय 'यह' है।
MeTTeO

7
हालांकि मैं समग्र रूप से सहमत हूं, मैं असहमत हूं कि यह "केवल एक ही चीज" का उल्लंघन करता है। लौटना thisमुश्किल से जटिल रॉकेट साइंस है। :-)
user949300

अतिरिक्त बिंदु: यह कमांड-क्वेरी पृथक्करण सिद्धांत का भी उल्लंघन करता है।
मार्टन_होन

84

मैं इसके लिए 'विधियों' का उपयोग करना पसंद करता हूं:

public String getFoo() { return foo; }
public void setFoo(String foo) { this.foo = foo; }
public Employee withFoo(String foo) {
  setFoo(foo);
  return this;
}

इस प्रकार:

list.add(new Employee().withName("Jack Sparrow")
                       .withId(1)
                       .withFoo("bacon!"));

चेतावनी : यह withXवाक्यविन्यास आमतौर पर अपरिवर्तनीय वस्तुओं के लिए "बसने" प्रदान करने के लिए उपयोग किया जाता है, इसलिए इन विधियों के कॉलर मौजूदा उदाहरण को म्यूट करने के बजाय नए ऑब्जेक्ट बनाने की अपेक्षा कर सकते हैं। शायद एक अधिक उचित शब्दांकन कुछ इस तरह होगा:

list.add(new Employee().chainsetName("Jack Sparrow")
                       .chainsetId(1)
                       .chainsetFoo("bacon!"));

चेनसेटएक्सवाईज () नामकरण सम्मेलन के साथ वस्तुतः सभी को खुश होना चाहिए।


18
दिलचस्प सम्मेलन के लिए +1। मैं इसे अपने कोड में अपनाने नहीं जा रहा हूं क्योंकि ऐसा लगता है कि अब आपके पास हर वर्ग के लिए एक get, एक setऔर एक है with। यह अभी भी एक दिलचस्प समाधान है, हालांकि। :)
पॉल मंटा

1
यह इस बात पर निर्भर करता है कि आप कितनी बार वासियों को बुलाते हैं। मैंने पाया है कि अगर उन लोगों को बहुत कुछ कहा जाता है, तो उन्हें जोड़ने के लिए अतिरिक्त परेशानी के लायक है क्योंकि यह हर जगह कोड को सरल करता है। YMMV
क्वालिडैफियल

2
और अगर आप को यह जोड़ा Project Lombokहै @Getter/@Setterएनोटेशन ... कि चेनिंग के लिए शानदार होगा। या आप Kestrelकॉम्बिनेटर ( github.com/raganwald/Katy ) जैसे कुछ का उपयोग कर सकते हैं जो JQuery और जावास्क्रिप्ट का उपयोग करता है।
एहतेश चौधरी

4
@ AlikElzin-kilaka वास्तव में मैंने अभी देखा है कि जावा 8 में java.time अपरिवर्तनीय कक्षाएं इस पैटर्न का उपयोग करती हैं, जैसे LocalDate.withMonth, withYear, इत्यादि
16

4
withउपसर्ग एक अलग सम्मेलन है। जैसा @qualidafial ने एक उदाहरण दिया। पहले से उपसर्ग किए गए तरीके withवापस नहीं आने चाहिए this, बल्कि वर्तमान उदाहरण की तरह एक नया उदाहरण है, लेकिन withयह बदल जाता है। यह तब किया जाता है जब आप अपनी वस्तुओं को अपरिवर्तनीय बनाना चाहते हैं। इसलिए जब मैं एक विधि देखता हूं, जो withमुझे लगता है कि मुझे एक नई वस्तु मिलेगी, तो वह एक ही वस्तु नहीं होगी।
टेम्पेक

26

यदि आप 'this'सेटर से वापस नहीं लौटना चाहते हैं , लेकिन दूसरे विकल्प का उपयोग नहीं करना चाहते हैं तो आप गुणों को सेट करने के लिए निम्नलिखित सिंटैक्स का उपयोग कर सकते हैं:

list.add(new Employee()
{{
    setName("Jack Sparrow");
    setId(1);
    setFoo("bacon!");
}});

एक तरफ के रूप में मुझे लगता है कि सी # में इसका थोड़ा क्लीनर:

list.Add(new Employee() {
    Name = "Jack Sparrow",
    Id = 1,
    Foo = "bacon!"
});

16
डबल ब्रेस इनिशियलाइज़ेशन में बराबरी की समस्या हो सकती है क्योंकि यह एक अनाम आंतरिक वर्ग बनाता है; देखें c2.com/cgi/wiki?DoubleBraceInitialization
Csaba_H

@Csaba_H स्पष्ट रूप से समस्या उस व्यक्ति की गलती है जिसने equalsविधि को उलझा दिया । अनाम कक्षाओं से निपटने के लिए बहुत साफ तरीके हैं equalsयदि आप जानते हैं कि आप क्या कर रहे हैं।
AJMansfield

उसके लिए एक नया (अनाम) वर्ग बनाना? हर उदाहरण के लिए?
user85421

11

यह न केवल गेटर्स / सेटर्स के सम्मेलन को तोड़ता है, यह जावा 8 विधि संदर्भ ढांचे को भी तोड़ता है। MyClass::setMyValuea है BiConsumer<MyClass,MyValue>, और myInstance::setMyValuea है Consumer<MyValue>। यदि आपके पास अपना सेटर रिटर्न है this, तो यह अब का एक वैध उदाहरण Consumer<MyValue>नहीं है Function<MyValue,MyClass>, बल्कि एक है , और उन बसने के लिए विधि संदर्भों का उपयोग करके कुछ भी कारण होगा (यह मानते हुए कि वे शून्य तरीके हैं) तोड़ने के लिए।


2
यह बहुत बढ़िया होगा यदि जावा के पास रिटर्न प्रकार से ओवरलोड करने का कोई तरीका है, न कि केवल जेवीएम। आप इनमें से कई ब्रेकिंग परिवर्तनों को आसानी से बायपास कर सकते हैं।
शोभनाथ

1
आप हमेशा एक कार्यात्मक इंटरफ़ेस परिभाषित कर सकते हैं जो दोनों का विस्तार करता है Consumer<A>और Function<A,B>एक डिफ़ॉल्ट कार्यान्वयन प्रदान करता है void accept(A a) { apply(a); }। फिर इसे आसानी से एक के रूप में इस्तेमाल किया जा सकता है और किसी भी कोड को नहीं तोड़ा जाएगा जिसके लिए एक विशिष्ट फॉर्म की आवश्यकता होती है।
स्टीव के

अरे! यह सीधे तौर पर गलत है! इसे शून्य-अनुकूलता कहा जाता है। एक सेटर जो एक मूल्य लौटाता है, एक उपभोक्ता के रूप में कार्य कर सकता है। ideone.com/ZIDy2M
माइकल

8

मैं जावा नहीं जानता, लेकिन मैंने यह C ++ में किया है। अन्य लोगों ने कहा है कि यह लाइनों को वास्तव में लंबी और वास्तव में पढ़ने के लिए कठिन बनाता है, लेकिन मैंने इसे बहुत बार ऐसा किया है:

list.add(new Employee()
    .setName("Jack Sparrow")
    .setId(1)
    .setFoo("bacon!"));

यह और भी बेहतर है:

list.add(
    new Employee("Jack Sparrow")
    .Id(1)
    .foo("bacon!"));

कम से कम, मुझे लगता है। लेकिन अगर आप चाहें तो मेरा स्वागत करें और मुझे एक भयानक प्रोग्रामर कहें। और मुझे नहीं पता कि आपको जावा में ऐसा करने की अनुमति है या नहीं।


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

आप शायद सही हैं ... मैंने केवल ऑटो-फॉर्मेटर का उपयोग किया है, यह एमएसीएस ऑटो इंडेंटिंग है।
कार्सन मायर्स

2
स्रोत कोड फॉर्मेटर्स को श्रृंखला में प्रत्येक विधि कॉल के बाद एक सरल // के साथ ज़ब्त किया जा सकता है। यह आपके कोड को थोड़ा छोटा कर देता है, लेकिन क्षैतिज रूप से सुधारे गए बयानों की आपकी ऊर्ध्वाधर श्रृंखला में उतना नहीं है।
क्वालिडैफिअल

@qualidafial आपको //प्रत्येक विधि के बाद की आवश्यकता नहीं है यदि आप अपनी आईडीई को पहले से ही लिपटे लाइनों (जैसे ग्रहण> गुण> जावा> कोड शैली> प्रारूप> रेखा लपेटन> कभी भी पहले से लिपटी लाइनों में शामिल नहीं) में शामिल होने के लिए कॉन्फ़िगर नहीं करते हैं।
डीजेडेवार्क

6

एक 'धाराप्रवाह इंटरफ़ेस' नामक यह योजना (वाक्य का उद्देश्य) अब काफी लोकप्रिय हो रही है। यह स्वीकार्य है, लेकिन यह वास्तव में मेरी चाय का कप नहीं है।


6

चूँकि यह शून्य नहीं है, इसलिए यह एक वैध JavaBean संपत्ति सेटर नहीं है। अगर आप दुनिया के सात लोगों में से एक हैं जो दृश्य "बीन बिल्डर" टूल का उपयोग कर रहे हैं, या 17 में से एक जेएसपी-बीन-सेटप्रॉपर्टी तत्वों का उपयोग कर रहा है।


यह भी मायने रखता है कि क्या आप स्प्रिंग की तरह सेम-जागरूक फ्रेमवर्क का उपयोग करते हैं।
ddimitrov

6

कम से कम सिद्धांत रूप में , यह कॉल के बीच गलत निर्भरता स्थापित करके JVM के अनुकूलन तंत्र को नुकसान पहुंचा सकता है।

इसे सिंटैक्टिक शुगर माना जाता है, लेकिन वास्तव में सुपर-इंटेलिजेंट जावा 43 की वर्चुअल मशीन में दुष्प्रभाव पैदा कर सकता है।

इसलिए मैं वोट नहीं, इसका उपयोग नहीं करते।


10
दिलचस्प ... क्या आप इस पर थोड़ा विस्तार कर सकते हैं?
केन लियू

3
जरा सोचिए कि सुपरसैलकर प्रोसेसर समानांतर निष्पादन से कैसे निपटते हैं। दूसरी setविधि को निष्पादित करने का उद्देश्य पहली setविधि पर निर्भर है, हालांकि यह प्रोग्रामर द्वारा जाना जाता है।
मारीअन

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

12
यदि आप नहीं जानते, परीक्षण करें। -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining Java7 jdk निश्चित रूप से जंजीरों के तरीकों को रेखांकित करता है, और लगभग उतने ही पुनरावृत्तियों को करता है जो शून्य बसंतों को गर्म के रूप में चिह्नित करते हैं और उन्हें भी इनलाइन करते हैं। जेवीएम के ओपकोड प्रूनिंग एल्गोरिदम की शक्ति को कम करके आपको मिथक करता है; अगर यह जानता है कि आप इसे वापस कर रहे हैं, तो यह jrs (java रिटर्न स्टेटमेंट) opcode को छोड़ देगा और इसे स्टैक पर छोड़ देगा।
अजाक्स

1
एंड्रियास, मैं सहमत हूं लेकिन जब आप अक्षम कोड की परत पर परतें हैं तो समस्याएं दिखाई देती हैं। 99% समय आपको स्पष्टता के लिए कोड करना चाहिए जो कि यहां बहुत कुछ कह जाता है। लेकिन ऐसे समय भी होते हैं जब आपको यथार्थवादी होने की आवश्यकता होती है और अपने वर्षों के अनुभव का उपयोग समय से पहले सामान्य वास्तुशिल्प अर्थ में अनुकूलित करने के लिए किया जाता है।
लीजेंडलग्रेव

6

यह एक बुरा अभ्यास नहीं है। लेकिन यह JavaBeans Spec के साथ कॉम्पैटिबल नहीं है ।

और बहुत सारे विनिर्देश हैं जो उन मानक एक्सेसरों पर निर्भर करते हैं।

आप हमेशा उन्हें एक-दूसरे के सह-अस्तित्व में बना सकते हैं।

public class Some {
    public String getValue() { // JavaBeans
        return value;
    }
    public void setValue(final String value) { // JavaBeans
        this.value = value;
    }
    public String value() { // simple
        return getValue();
    }
    public Some value(final String value) { // fluent/chaining
        setValue(value);
        return this;
    }
    private String value;
}

अब हम उन्हें एक साथ उपयोग कर सकते हैं।

new Some().value("some").getValue();

यहाँ अपरिवर्तनीय वस्तु के लिए एक और संस्करण आता है।

public class Some {

    public static class Builder {

        public Some build() { return new Some(value); }

        public Builder value(final String value) {
            this.value = value;
            return this;
        }

        private String value;
    }

    private Some(final String value) {
        super();
        this.value = value;
    }

    public String getValue() { return value; }

    public String value() { return getValue();}

    private final String value;
}

अब हम ऐसा कर सकते हैं।

new Some.Builder().value("value").build().getValue();

2
मेरा संपादन अस्वीकृत हो गया, लेकिन आपका बिल्डर उदाहरण सही नहीं है। पहला, .value () कुछ भी वापस नहीं करता है, और यह someफ़ील्ड भी सेट नहीं करता है । दूसरे, आप एक रक्षा और सेट जोड़ना चाहिए someकरने के लिए nullनिर्माण में () तो Someसही मायने में अपरिवर्तनीय है, अन्यथा आप कह सकते हैं builder.value()फिर से वही बिल्डर उदाहरण पर। और अंत में, हां आपके पास एक बिल्डर है, लेकिन आपके पास Someअभी भी एक सार्वजनिक निर्माणकर्ता है, जिसका अर्थ है कि आप खुले तौर पर बिल्डर के उपयोग की वकालत नहीं करते हैं, अर्थात उपयोगकर्ता इसके बारे में नहीं जानता है या कस्टम सेट करने के लिए कोई तरीका नहीं खोज रहा है। valueबिल्कुल भी।
अदोव्रत

@Andrath यदि उत्तर गलत है, तो आपको अपना उत्तर लिखना चाहिए, कोशिश नहीं करनी चाहिए और किसी और के आकार को संपादित करना चाहिए
CalvT

1
@ जीनकॉन ग्रेट। धन्यवाद! और खेद है कि अगर मैं पहले असभ्य लग रहा था।
Adowrath

1
@Adowrath कृपया संवर्द्धन के लिए किसी भी अतिरिक्त टिप्पणी के लिए स्वतंत्र महसूस करें। FYI करें, मैं वह नहीं हूं जिसने आपके संपादन को अस्वीकार कर दिया है। :)
जिन क्वाँ

1
मैं जानता हूँ मैं जानता हूँ। ^ ^ और नए संस्करण के लिए धन्यवाद, कि अब एक वास्तविक परिवर्तनशील "अपरिवर्तनीय-कुछ" बिल्डर प्रदान करता है। और यह मेरे संपादन प्रयासों की तुलना में अधिक चतुर समाधान है, जो कि तुलना में, कोड को बरबाद कर देता है।
Adowrath

4

यदि आप पूरे आवेदन पत्र में एक ही सम्मेलन का उपयोग करते हैं तो यह ठीक लगता है।

यदि आपके आवेदन का मौजूदा हिस्सा मानक कन्वेंशन का उपयोग करता है तो ओहर के हाथ पर मैं इसे चिपका देता हूं और बिल्डरों को अधिक जटिल कक्षाओं में जोड़ता हूं

public class NutritionalFacts {
    private final int sodium;
    private final int fat;
    private final int carbo;

    public int getSodium(){
        return sodium;
    }

    public int getfat(){
        return fat;
    }

    public int getCarbo(){
        return carbo;
    }

    public static class Builder {
        private int sodium;
        private int fat;
        private int carbo;

        public Builder sodium(int s) {
            this.sodium = s;
            return this;
        }

        public Builder fat(int f) {
            this.fat = f;
            return this;
        }

        public Builder carbo(int c) {
            this.carbo = c;
            return this;
        }

        public NutritionalFacts build() {
            return new NutritionalFacts(this);
        }
    }

    private NutritionalFacts(Builder b) {
        this.sodium = b.sodium;
        this.fat = b.fat;
        this.carbo = b.carbo;
    }
}

1
यह बिल्कुल वही है जो बिल्डर पैटर्न को ठीक करने के लिए डिज़ाइन किया गया था। यह जावा 8 में लैम्ब्डा को नहीं तोड़ता है, यह बारीक जावाबीन्स टूल्स को नहीं तोड़ता है, और यह जेवीएम में किसी भी अनुकूलन मुद्दे का कारण नहीं बनता है (चूंकि ऑब्जेक्ट केवल तात्कालिकता के दौरान मौजूद है)। यह "जिस तरह से कई बिल्डरों" समस्या को हल करता है जो आपको बस बिल्डरों का उपयोग नहीं करने के साथ-साथ दोहरे ब्रेस अनाम वर्गों से ढेर प्रदूषण को खत्म करने से मिलता है।
ndm13

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

4

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



3

मैं बसने वालों के पक्ष में हूं कि यह "रिटर्न" है। मुझे परवाह नहीं है अगर यह सेम अनुरूप नहीं है। मेरे लिए, यदि "=" अभिव्यक्ति / कथन होना ठीक है, तो यह तय करता है कि रिटर्न मान ठीक है।


2

मैं इस दृष्टिकोण को पसंद करता था लेकिन मैंने इसके खिलाफ फैसला किया है।

कारण:

  • पठनीयता। यह एक अलग लाइन पर प्रत्येक सेटफू () के लिए कोड को अधिक पठनीय बनाता है। आप आमतौर पर कोड को कई बार पढ़ते हैं, एक बार लिखने से कई गुना अधिक।
  • साइड इफेक्ट: setFoo () केवल फ़ील्ड फू सेट करना चाहिए, और कुछ नहीं। यह लौटाना एक अतिरिक्त "व्हाट इज दैट" था।

बिल्डर पैटर्न मैंने देखा कि सेटफू (फू) .सेटबेर (बार) सम्मेलन का उपयोग न करें लेकिन अधिक फू (फू) .बार (बार)। शायद उन कारणों के लिए।

यह हमेशा की तरह स्वाद की बात है। मुझे "कम से कम आश्चर्य" दृष्टिकोण पसंद है।


2
मैं साइड इफेक्ट पर सहमत हूं। सामान लौटाने वाले सेट उनके नाम का उल्लंघन करते हैं। आप फू सेट कर रहे हैं, लेकिन आपको कोई वस्तु वापस मिल रही है? क्या यह एक नई वस्तु है या मैंने पुराने को बदल दिया है?
crunchdog

2

इस विशेष पैटर्न को मेथड चाइनिंग कहा जाता है। विकिपीडिया लिंक , इसमें अधिक स्पष्टीकरण और उदाहरण हैं कि यह विभिन्न प्रोग्रामिंग भाषाओं में कैसे किया जाता है।

पुनश्च: बस इसे यहाँ छोड़ने के बारे में सोचा, क्योंकि मैं विशिष्ट नाम की तलाश में था।


1

पहली नजर में: "गस्टली!"।

आगे के विचार पर

list.add(new Employee().setName("Jack Sparrow").setId(1).setFoo("bacon!"));

की तुलना में वास्तव में कम त्रुटि प्रवण है

Employee anEmployee = new Employee();
anEmployee.setName("xxx");
...
list.add(anEmployee);

तो काफी दिलचस्प। टूलबैग में विचार जोड़ना ...


1
नहीं, यह अभी भी भयावह है। रखरखाव के दृष्टिकोण से, उत्तरार्द्ध बेहतर है क्योंकि इसे पढ़ना आसान है। इसके अतिरिक्त, CheckStyle जैसे स्वचालित कोड चेकर्स डिफ़ॉल्ट रूप से लाइनों को 80 वर्णों तक लागू करेंगे - कोड पठनीयता / रखरखाव समस्या को कम करने, कोड वैसे भी लपेटा जाएगा। और अंत में - यह जावा है; जब भी यह बाइट कोड के लिए संकलित होने जा रहा है, तो एक ही लाइन पर सब कुछ लिखने का कोई लाभ नहीं है।
OMG पोंजी

1
व्यक्तिगत रूप से, मुझे लगता है कि पूर्व को पढ़ना आसान है, खासकर यदि आप इस तरह से कई ऑब्जेक्ट बना रहे हैं।
केन लियू

@ केन: एक विधि कोड। धाराप्रवाह प्रारूप में एक प्रति लिखें; दूसरे में एक और कॉपी। अब एक दो लोगों को दो प्रतियां दें, और पूछें कि उन्हें कौन सा पढ़ना आसान लगता है। पढ़ने के लिए तेजी से, कोड के लिए तेजी से।
OMG पोंजी

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

2
यह ठीक होगा यदि प्रत्येक डॉट और इंडेंट से पहले लाइनब्रीक थे। फिर, यह दूसरे संस्करण के रूप में पठनीय होगा। वास्तव में अधिक है, क्योंकि listशुरुआत में कोई अतिरेक नहीं है ।
18 अगस्त को MauganRa

1

हां, मुझे लगता है कि यह एक अच्छा विचार है।

अगर मैं कुछ जोड़ सकता हूँ, इस समस्या के बारे में क्या:

class People
{
    private String name;
    public People setName(String name)
    {
        this.name = name;
        return this;
    }
}

class Friend extends People
{
    private String nickName;
    public Friend setNickName(String nickName)
    {
        this.nickName = nickName;
        return this;
    }
}

यह काम करेगा:

new Friend().setNickName("Bart").setName("Barthelemy");

यह ग्रहण द्वारा स्वीकार नहीं किया जाएगा! :

new Friend().setName("Barthelemy").setNickName("Bart");

इसका कारण यह है कि setName () लोगों को लौटाता है और मित्र को नहीं, और PeoplesetNickName नहीं है।

वर्ग के नाम के बजाय हम SELF वर्ग को वापस भेजने के लिए कैसे लिख सकते हैं?

ऐसा कुछ ठीक होगा (यदि SELF कीवर्ड मौजूद होगा)। क्या यह वैसे भी मौजूद है?

class People
{
    private String name;
    public SELF setName(String name)
    {
        this.name = name;
        return this;
    }
}

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

5
जेनरिक का उपयोग करें। क्लास चैनेबल <सेल्फ चैनटेबल> {पब्लिक सेल्फ डूसमेल्ट () {रिटर्न (सेल्फ) इस;}} को बढ़ाता है। यह तकनीकी रूप से सुरक्षित नहीं है (यदि आप सेल्फ टाइप के साथ क्लास को लागू नहीं कर सकते हैं तो यह क्लास कास्ट हो जाएगा) सही व्याकरण है, और उपवर्ग अपने प्रकार वापस कर देंगे।
अजाक्स

इसके अलावा: बैपटिस्ट का उपयोग करने वाले इस "SELF" को एक स्व प्रकार कहा जाता है, कई भाषाओं में मौजूद नहीं है (स्काला वास्तव में एकमात्र ऐसा है जिसके बारे में मैं अभी सोच सकता हूं), जहां अजाक्स का जेनरिक का उपयोग तथाकथित रूप से "उत्सुकता से" है। आवर्ती टेम्पलेट पैटर्न ", जो आत्म प्रकार की कमी का सामना करने की कोशिश करता है, लेकिन इसमें कुछ कमियां हैं: ( class C<S extends C<S>>जो, एक सादे से अधिक सुरक्षित है S extends C। एक) यदि A extends B, और B extends C<B>, और आपके पास एक है, तो आप केवल यह जानते हैं कि। जब तक A प्रत्येक और प्रत्येक विधि को ओवरराइड नहीं करता, तब तक B को वापस कर दिया जाता है। बी) आप एक स्थानीय, गैर-कच्चे को निरूपित नहीं कर सकते C<C<C<C<C<...>>>>>
Adowrath

1

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


2
त्रुटि स्थिति को इंगित करने के लिए अपवादों का उपयोग करने के बारे में कैसे? त्रुटि कोड को आसानी से अनदेखा किया जा सकता है, क्योंकि कई सी प्रोग्रामर ने दर्दनाक तरीके से सीखा है। अपवाद स्टैक को उस बिंदु तक बुलबुला कर सकते हैं जहां उन्हें संभाला जा सकता है।
ddimitrov

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

1
हाय @Narek, शायद आप इस बात को विस्तृत कर सकते हैं कि किन मामलों में अपवादों के बजाय त्रुटि कोड का उपयोग करना बेहतर है और अपवाद क्यों?
ddimitrov

0

कथन से

list.add(new Employee().setName("Jack Sparrow").setId(1).setFoo("bacon!"));

मैं दो चीजें देख रहा हूं

१) व्यर्थ कथन। 2) पठनीयता का अभाव।


0

यह कम पठनीय हो सकता है

list.add(new Employee().setName("Jack Sparrow").setId(1).setFoo("bacon!")); 

या यह

list.add(new Employee()
          .setName("Jack Sparrow")
          .setId(1)
          .setFoo("bacon!")); 

यह इस तरह से अधिक पठनीय है:

Employee employee = new Employee();
employee.setName("Jack Sparrow")
employee.setId(1)
employee.setFoo("bacon!")); 
list.add(employee); 

8
मुझे लगता है कि अगर आप एक पंक्ति में अपने सभी कोड डालने की कोशिश नहीं करते हैं तो यह बहुत ही पठनीय है।
केन लियू

0

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

उदाहरण के लिए, मैंने इसे सुनिश्चित करने के लिए परीक्षण नहीं किया है, लेकिन मुझे आश्चर्य नहीं होगा कि जैक्सन उन लोगों को पहचान नहीं पाएगा जब आप java ऑब्जेक्ट्स को json / मैप्स से बनाते हैं। मुझे उम्मीद है कि मैं इस पर गलत हूं (मैं जल्द ही इसका परीक्षण करूंगा)।

वास्तव में, मैं एक हल्के एसक्यूएल केंद्रित ओआरएम विकसित कर रहा हूं और मुझे इसे वापस करने वाले मान्यता प्राप्त निपटानकर्ताओं के लिए कुछ कोड beyong getPropertyDescriptors जोड़ना होगा।


0

बहुत पहले जवाब, लेकिन मेरे दो सेंट ... इसका ठीक। काश यह धाराप्रवाह इंटरफ़ेस अधिक बार उपयोग किया जाता।

'फ़ैक्टरी' चर को दोहराने से अधिक जानकारी नीचे नहीं दी जाती है:

ProxyFactory factory = new ProxyFactory();
factory.setSuperclass(Foo.class);
factory.setFilter(new MethodFilter() { ...

यह क्लीनर है, इम्हो:

ProxyFactory factory = new ProxyFactory()
.setSuperclass(Properties.class);
.setFilter(new MethodFilter() { ...

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


0

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

यह दृष्टिकोण आपके क्लाइंट कोड को अनुमति देता है:

  • वापसी प्रकार के प्रति उदासीन
  • बनाए रखने के लिए आसान है
  • संकलक दुष्प्रभावों से बचें

यहाँ कुछ उदाहरण हैं।

val employee = Employee().apply {
   name = "Jack Sparrow"
   id = 1
   foo = "bacon"
}


val employee = Employee()
with(employee) {
   name = "Jack Sparrow"
   id = 1
   foo = "bacon"
}


val employee = Employee()
employee.let {
   it.name = "Jack Sparrow"
   it.id = 1
   it.foo = "bacon"
}

0

यदि मैं एक एपीआई लिख रहा हूं, तो मैं मानों को सेट करने के लिए "इसे वापस करता हूं" का उपयोग करता हूं जो केवल एक बार सेट किया जाएगा। यदि मेरे पास कोई अन्य मान है जो उपयोगकर्ता को बदलने में सक्षम होना चाहिए, तो मैं इसके बजाय एक मानक शून्य सेटर का उपयोग करता हूं।

हालांकि, यह वास्तव में वरीयता का मामला है और बसने वाले लोगों को मेरी राय में, बहुत अच्छा लगता है।


0

मैं सभी पोस्टरों से सहमत हूं जो दावा करता है कि यह जावाबीन की युक्ति को तोड़ता है। इसे संरक्षित करने के कारण हैं, लेकिन मुझे यह भी लगता है कि इस बिल्डर पैटर्न के उपयोग (जो कि सभी को बताया गया था) में इसका स्थान है; जब तक यह हर जगह उपयोग नहीं किया जाता है, तब तक यह स्वीकार्य होना चाहिए। "इट्स प्लेस", मेरे लिए, वह जगह है जहां अंतिम बिंदु "बिल्ड ()" विधि के लिए एक कॉल है।

बेशक इन सभी चीजों को स्थापित करने के अन्य तरीके हैं, लेकिन यहां लाभ यह है कि यह 1) कई-पैरामीटर सार्वजनिक निर्माणकर्ताओं और 2) आंशिक रूप से निर्दिष्ट वस्तुओं से बचा जाता है। यहां, आपके पास बिल्डर संग्रह है जो आवश्यक है और फिर इसके "बिल्ड ()" को अंत में कॉल करें, जो तब यह सुनिश्चित कर सकता है कि आंशिक रूप से निर्दिष्ट ऑब्जेक्ट का निर्माण नहीं किया गया है, क्योंकि उस ऑपरेशन को कम-से-सार्वजनिक दृश्यता दी जा सकती है। विकल्प "पैरामीटर ऑब्जेक्ट्स" होगा, लेकिन IMHO समस्या को केवल एक स्तर पर धकेलता है।

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

संक्षेप में, मुझे लगता है कि यह एक अच्छा अभ्यास है, अगर इसका सही उपयोग किया जाए।


-4

बुरी आदत: एक सेटर एक गेट्टर सेट मिलता है

क्या स्पष्ट रूप से एक विधि की घोषणा के बारे में, कि यह यू के लिए करता है

setPropertyFromParams(array $hashParamList) { ... }

ऑटो-पूर्ति और रीफैक्टरिंग और पठनीयता ए बी बॉयलरप्लेट कोड के लिए अच्छा नहीं है
एंड्रियास डिट्रीच

क्योंकि: 1) आपको ऑर्डर याद रखना है या डॉक्स से पढ़ना है, 2) आप सभी कंपाइल-टाइम टाइप सेफ्टी को ढीला कर देते हैं, 3) आपको वैल्यू कास्ट करना होगा (यह और 2) डायनेमिक में एक नॉन प्रॉब्लम है पाठ्यक्रम की भाषा), 4) आप इसे हर इनवोकेशन पर ऐरे-लेंथ की जाँच करने के अलावा अन्य उपयोगी रूप से नहीं बढ़ा सकते हैं , और ५) यदि आप कुछ भी बदलते हैं, तो यह क्रम हो या कुछ मूल्यों का अस्तित्व हो, आप यह नहीं देख सकते हैं कि न तो रनटाइम और न ही timewithout संदेह संकलन सब पर । बसने वालों के साथ: 1), 2), 3): कोई समस्या नहीं है। 4) नए तरीकों को जोड़ने से कुछ भी नहीं टूटता है। 5) स्पष्ट त्रुटि संदेश।
शोभनाथ
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.