2010-09-13 10 views
8

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

इस तरह से मैं

MyImmutable i1 = new MyImmutable.Builder().foo(1).bar(2).build(); 
MyImmutable i2 = i1.builder().foo(3).build(); 

जा सकते हैं Cloneable इंटरफेस कुछ हद तक तोड़ा जा करने के लिए कहा है, लेकिन इस के किसी भी अच्छा जावा कोडिंग अभ्यास का उल्लंघन करता है, इस निर्माण के साथ कोई समस्या है?

final class MyImmutable { 
    public int foo() { return builder.foo; } 
    public int bar() { return builder.bar; } 
    public Builder builder() { return builder.clone(); } 
    public static final class Builder implements Cloneable { 
    public Builder foo(int val) { foo = val; return this; } 
    public Builder bar(int val) { bar = val; return this; } 
    public MyImmutable build() { return new MyImmutable(this.clone()); } 
    private int foo = 0; 
    private int bar = 0; 
    @Override public Builder clone() { try { return (Builder)super.clone(); } catch(CloneNotSupportedException e) { throw new AssertionError(); } } 
    } 
    private MyImmutable(Builder builder) { this.builder = builder; } 
    private final Builder builder; 
} 

उत्तर

5

आम तौर पर बिल्डर से निर्मित कक्षा में निर्माता का कोई विशेष ज्ञान नहीं है। यही कारण है कि एक निर्माता foo और बार के लिए मूल्य की आपूर्ति करने के लिए होता है अपरिवर्तनीय है:

public final class MyImmutable { 
    public final int foo; 
    public final int bar; 
    public MyImmutable(int foo, int bar) { 
    this.foo = foo; 
    this.bar = bar; 
    } 
} 

बिल्डर एक अलग वर्ग होगा:

public class MyImmutableBuilder { 
    private int foo; 
    private int bar; 
    public MyImmutableBuilder foo(int val) { foo = val; return this; } 
    public MyImmutableBuilder bar(int val) { bar = val; return this; } 
    public MyImmutable build() { return new MyImmutable(foo, bar); } 
} 

यदि आप चाहते हैं तो आप MyImmutable करने के लिए एक स्थिर विधि जोड़ सकते हैं बिल्डर एक मौजूदा MyImmutable उदाहरण से शुरू करने के लिए:

public static MyImmutableBuilder basedOn(MyImmutable instance) { 
    return new MyImmutableBuilder().foo(instance.foo).bar(instance.bar); 
} 
+0

मैं कुछ कोनों को काटने और स्पष्ट रूप से फ़ील्ड की प्रतिलिपि बनाने से बचने की कोशिश कर रहा था। "आधारितऑन" विधि वास्तव में स्पष्ट है लेकिन मुझे फ़ील्ड (दोबारा) कॉपी करने की आवश्यकता होगी। शायद मैं बहुत आलसी हूं। – Aksel

+0

ग्रेट सुझाव ऑनऑन विधि :) – troig

2

मैंने पहले इस दृष्टिकोण को नहीं देखा है, लेकिन ऐसा लगता है कि यह ठीक काम करेगा।

असल में यह बिल्डर पैटर्न को अपेक्षाकृत सरल बनाता है, थोड़ा अधिक रनटाइम ओवरहेड (अतिरिक्त ऑब्जेक्ट्स + क्लोनिंग ऑपरेशंस + एक्सेसर फ़ंक्शंस में इंडिकेशन का स्तर जो संकलित हो सकता है या नहीं)।

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

+0

निर्माता को अपरिवर्तनीय बनाना एक दिलचस्प सुझाव है, लेकिन शायद मुझे पत्र में उत्कृष्ट उदाहरण जोशुआ ब्लॉच का पालन करना चाहिए और चालाक होने की कोशिश करना बंद करना चाहिए। ठीक है कि गलत हो गया, स्पष्ट रूप से यहोशू ब्लोच बहुत चालाक है। मेरा मतलब था कि उसकी उत्कृष्ट पुस्तक के आइटम 2 का पालन करें। – Aksel

+0

संकेत के अनुसार दृष्टिकोण के साथ, एक अलग अपरिवर्तनीय वस्तु से किसी भी प्रकार के परिवर्तनों के साथ एक नई अपरिवर्तनीय वस्तु का उत्पादन करने के लिए दो रक्षात्मक प्रतियों की आवश्यकता होगी।एक परिवर्तनीय निर्माता के बिना, एन परिवर्तन करने के लिए एन रक्षात्मक प्रतियों की आवश्यकता होगी। कौन सा दृष्टिकोण बेहतर होगा, किए जाने वाले बदलावों की संख्या पर निर्भर करेगा। – supercat

2

आपका कार्यान्वयन कार्यान्वयन जोश बलोच के प्रभावी जावा 2 संस्करण में विस्तृत के समान है।

विवाद का एक बिंदु आपकी build() विधि होगी। यदि एक एकल निर्माता एक अपरिवर्तनीय उदाहरण बनाता है, तो क्या यह काम करने के लिए निर्माता को फिर से उपयोग करने की अनुमति देना उचित होगा? यहां सावधानी बरतनी है कि भले ही आप एक अपरिवर्तनीय वस्तु बना रहे हों, फिर भी आपके निर्माता की उत्परिवर्तन के परिणामस्वरूप कुछ "आश्चर्यजनक" बग हो सकती हैं।

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

+0

"प्रभावी जावा" इसे लाभ के रूप में नोट करता है: "एक एकल निर्माता का उपयोग कई ऑब्जेक्ट्स बनाने के लिए किया जा सकता है। निर्माता के पैरामीटर ऑब्जेक्ट क्रिएशन के बीच वस्तुओं को बदलने के लिए tweaked किया जा सकता है।" – slim

+0

हां, यह है। जो आप नहीं चाहते हैं वह निर्माता का पिछला उपयोग उस नई ऑब्जेक्ट में हस्तक्षेप करने के लिए है जिसे वर्तमान में बनाने के लिए उपयोग किया जा रहा है। – gpampara

संबंधित मुद्दे