2015-06-12 2 views
6
public class App { 
    private final A a; 
    private final Server server; 
    public App(){ 
     a = new A(this); //Bad, this is escaping before it's initialized. 
    } 
    @Subscribe //This event fires some time after App is finished constructing. 
    public void registerStuff(RegisterEvent event){ 
     server = event.getServer(); //Not possible due to final field and this not being the constructor, is there such thing as a lazy final? 
     a.register(); 
    } 
} 

public class A { 
    private final App app; 
    private final BuilderSpec spec; 
    public A(App app){ 
     this.app = app; 
     this.spec = app.getServer().builder(this.app).build(); 
    } 
    public void register(){ 
     app.getServer().doStuff(this.app, this.spec); 
    } 
} 

मैंने "यह" भागने के बारे में कुछ पढ़ा है और महसूस किया है कि पिछला कोड खराब है, क्योंकि मुझे पता नहीं है कि बाहरी प्रक्रियाएं क्या कर रही हैं यह संदर्भ, इसलिए इसे निर्माणकर्ता के बाहर तब तक पारित नहीं किया जाना चाहिए जब तक कि इसका निर्माण न हो जाए।अंतिम फ़ील्ड के साथ निर्माण के दौरान "इस" से बचने से रोकना

हालांकि, ऐप और ए दोनों में अंतिम फ़ील्ड के कारण, मैं वास्तव में नहीं देखता कि मैं इसे बाद में, या आलसी कैसे शुरू कर सकता हूं। खेतों को आखिरी कम महत्वपूर्ण बना रहा है तो यह बच निकल रहा है? मुझे यकीन नहीं है।

+1

यह स्वचालित रूप से खराब नहीं है, और आपके पास क्या ठीक है। आपको केवल सावधान रहना होगा कि आपके ऑब्जेक्ट को कन्स्ट्रक्टर से बाहर करने से पहले एक स्थिर स्थिति में है। इसे टालना है, हां, लेकिन यदि आपके पास गोलाकार निर्भरता है (जैसा कि आप करते हैं) और दोनों आइटम 'अंतिम' क्या हैं तो कोई विकल्प नहीं है। –

+0

ऐसी गोलाकार निर्भरता होने के नाते मुझे लगता है कि बहुत अच्छा नहीं है ... नहीं ?? – Codebender

+1

@ एबिशेक मानोहरन परिपत्र निर्भरता पूरी तरह से प्राकृतिक हैं। वे पूरी तरह से व्यवस्थित रूप से हर समय होते हैं और निश्चित रूप से सामान्य ओओ मॉडलिंग का हिस्सा हैं। –

उत्तर

2

अगर वे साइकिल चलाते हैं तो कन्स्ट्रक्टर निर्भरता एक समस्या बन सकती है। ऑब्जेक्ट इंस्टेंटेशन के साथ समस्याओं से बचने के लिए अधिकांश आईओसी कंटेनर पोस्ट-निर्माण विधियों के साथ काम करते हैं।

अपने कोड में प्रिंसिपल को लागू करना, यह देखने के लिए की तरह

class App { 
    private A a; 
    public App() { 
     // not necessary to have, just to show that we have no logic inside 
    } 
    protected void init() { // choose access modifiers 
     // init something in App; 
     a = new A(this); // safe to pass this now.   
    } 
} 

class A { 
    private final App app; 

    public A(App app) { 
     this.app = app; 
     // do whatever you need 
     // if logic affects other components, create init() method here 
    }  
} 

हाँ, App में एक संदर्भ नहीं अंतिम है जाएगा। लेकिन यह डिजाइन अधिक प्राकृतिक और कम त्रुटि प्रवण है।

+0

पीएस में कोड कैसा दिखता है मामला? यह बेहतर लगता है कि ए के पास ऐप का अंतिम संदर्भ होगा और यदि आवश्यक हो तो उसे पुन: स्थापित किया जाए, फिर संभवतः एक संभावित रूप से बदल रहे ऐप संदर्भ हो जो ए के राज्य को डी-सिंक कर सके। –

+0

@RyanTheLeach हाँ, मुझे लगता है कि अंतिम ऐप बेहतर है, मैं कोड संपादित करूंगा। – AdamSkywalker

+0

init() सामग्री को पंजीकृत/प्रारंभ करने के लिए @ सदस्यता छोड़ने वाले ईवेंट हैंडलर से क्या कहा जाएगा, उससे बहुत समान दिखने लग रहा है। क्या इस तर्क को इनिट विधि में कन्स्ट्रक्टर में रखने में कोई अंतर है?ऐसा लगता है कि मैंने पहले की किसी भी समस्या की तरह अब हस्तांतरित किया है कि ऑब्जेक्ट निर्माण समाप्त होने के बजाय ऑब्जेक्ट प्रारंभ करना समाप्त हो रहा है या नहीं? –

1

जैसा कि @Boris_the_Spider का उल्लेख है, this को कन्स्ट्रक्टर में पास करना आवश्यक नहीं है यदि आप सुनिश्चित करते हैं कि आपकी कक्षा एक सतत स्थिति में है (Passing “this” in java constructor देखें)। हालांकि, क्योंकि इस मामले बातों में भविष्य में App विस्तार के लिए सावधान रहना बहुत गलत हो सकता है (Don't publish the "this" reference during construction देखें)

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

public class App { 
    private A a; 
    private final Server server; 
    public App(){} 

    // Define getter 
    private A getA(){ 
     return a == null ? new A(this) : a; 
    } 

    @Subscribe 
    public void registerStuff(RegisterEvent event){ 
     server = event.getServer(); 

     // Use getter 
     getA().register(); 
    } 
} 
+0

(-1 मेरा नहीं) ऐसा प्रतीत होता है कि 'रजिस्टरस्टफ'' नया ए() 'द्वारा ट्रिगर किया गया है :) गंभीर समस्या यह है कि 'ए' और 'ए' के ​​प्रारंभ के दौरान एक रहस्यमय 'सर्वर' ऑब्जेक्ट की आवश्यकता होती है, फिर भी यह केवल पतली हवा से बाहर आ सकता है। – ZhongYu

+0

@ bayou.io क्या यह सच है? ऐसा लगता है कि 'ए की एकमात्र विधि', रजिस्टर() 'को' रजिस्टरस्टफ ' – bcorso

+0

के अंदर बुलाया गया है क्षमा करें मुझे रजिस्टरवेन्ट के लिए कुछ संदर्भ प्रदान करना चाहिए था। ऐप का निर्माण करने के कुछ समय बाद रजिस्टरवेन्ट होता है, और जब निर्माता समाप्त नहीं होता है तब ट्रिगर नहीं होता है। –

1

आपका कोड संकलन नहीं है जब से तुम डॉन ' Server server वैरिएबल प्रारंभ करें जो final है। वैसे भी आप Provider पास कर सकते हैं जिसमें App को A पर उपलब्ध होने पर (पूरी तरह से प्रारंभ किया गया) प्रदान करने का काम है। आपका कोड इस तरह देख सकते हैं:

public App(){ 
    appProvider = new Provider<App>() 
    { 
     private volatile App app; 

     // setter here 

     @Override 
     public String get() 
     { 
      return app; 
     } 
    }; 
    a = new A(appProvider); 

} 

@Subscribe 
public void registerStuff(RegisterEvent event){ 
    // set server to your App 
    appProvider.set(this); 
} 

तो आप A में देख सकते हैं कि प्रदाता के get रिटर्न null या नहीं। मेरा मानना ​​है कि javax.inject पैकेज में आपके लिए उपयुक्त Provider इंटरफ़ेस है।

registerStuff विधि App प्रदान करने के लिए अच्छा लग रहा है क्योंकि उस बिंदु पर यह पूरी तरह से प्रारंभ किया गया है। यह आलसी प्रारंभिकता के बारे में आपके प्रश्न का भी उत्तर देता है।

+0

हां मुझे पता है कि प्रश्न में वर्तमान कोड संकलित नहीं होता है, लेकिन यह मेरी समस्या को स्पष्ट रूप से दिखाता है। –

+0

'ए' केवल 'सर्वर' का उपयोग नहीं करता है, इसे बिल्डर के लिए 'ऐप' की आवश्यकता होती है:' this.spec = app.getServer()। Builder (this.app) .build(); ' – bcorso

+0

मैं देखता हूं। आप 'ऐप' के लिए 'प्रदाता' लिख सकते हैं और जब भी आपके पास 'सर्वर' तैयार हो, तो 'ऐप' प्रदान करें। –

1

क्या आपको App कक्षा की भी आवश्यकता है? A केवल Server की आवश्यकता है। बस registerStuff() को App से A पर ले जाएं और केवल A का उपयोग करें।

public class A{ 



@Subscribe 
public void registerStuff(RegisterEvent event){ 
    Server server = event.getServer(); 
    BuilderSpec spec = server.builder(this) // not sure what `builder()` needs but might only be A ? 
           .build(); 

    server.doStuff(this, spec); 
} 
} 
+0

बिल्कुल मुझे एक "ऐप" कक्षा की आवश्यकता है, ऐप पूरी प्लगइन के लिए प्रवेश बिंदु है। यह निर्माण हो जाता है और फिर घटनाओं को प्राप्त करने के लिए जिम्मेदार होता है, या कक्षाओं को पंजीकृत करता है जो इसके बजाय घटनाएं प्राप्त करेंगे। –

+0

मैंने यही सोचा लेकिन यह लाइन इसे मार देती है: 'this.spec = app.getServer()। Builder (this.app) .build()' –

+0

क्या ऐप के अन्य फ़ील्ड हैं? – dkatzel

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