2016-02-20 7 views
5

में हुक की सुरक्षा मैं जावा में टेम्पलेट पैटर्न को कार्यान्वित कर रहा हूं। हमें कोड का निम्न भाग मान लेते हैं:टेम्पलेट पैटर्न

public abstract class A { 

    public final void work() { 
    doPrepare(); 
    doWork(); 
    } 

    protected abstract void doPrepare(); 

    protected abstract void doWork(); 
} 

public final class B extends A { 

    @Override 
    protected abstract void doPrepare() { /* do stuff here */ } 

    @Override 
    protected abstract void doWork() { /* do stuff here */ } 
} 

public final class Main { 

    public static void main(String[] args) { 
    B b = new B(); 
    b.work(); 
    } 
} 

मैं इसे एक समस्या पर विचार है कि एक आसानी से गलती से हमेशा b.work() कॉल करने के बजाय b.doWork() कॉल करने के लिए सक्षम है। हुकों को "छिपाने" के लिए, यदि संभव हो तो सबसे सुरुचिपूर्ण समाधान क्या है?

+0

"एक आसानी से गलती से b.doWork फोन करने में सक्षम है()"। वास्तव में? यह संरक्षित है, इसलिए केवल ए या बी उदाहरण इसे कॉल कर सकते हैं। –

+0

दुर्भाग्यवश, चूंकि यह संरक्षित है, पैकेज से सभी वर्ग इसे कॉल कर सकते हैं। विभाजन पैकेज एक तरीका है, हालांकि मैं 1- या 2-वर्ग पैकेज होने से बचाना चाहता हूं। –

+0

नहीं !! आप डिफ़ॉल्ट एक्सेस संशोधक का वर्णन कर रहे हैं (यानी कोई नहीं)। संरक्षित मतलब है "केवल मुझे या कक्षाएं जो मुझे विस्तारित करती हैं, इसका उपयोग इस" –

उत्तर

2

सबसे आसान और अधिक स्पष्ट बात तुम कर सकते हो एक और पैकेज से अपने A -> B पदानुक्रम उपयोग करने के लिए किया जाएगा।

यदि किसी कारण से आप इसे नहीं कर सकते हैं, तो आप एक इंटरफ़ेस का उपयोग कर सकते हैं और B कक्षा को उस श्रेणी को लपेट सकते हैं जो वास्तव में A से निकलती है। कुछ इस तरह:

public interface Worker { 

    void work(); 
} 

public abstract class A implements Worker { 

    @Override 
    public final void work() { 
     doPrepare(); 
     doWork(); 
    } 

    protected abstract void doPrepare(); 

    protected abstract void doWork(); 
} 

public static class B implements Worker { // does not extend A, but implements Worker 

    private final A wrapped = new A() { // anonymous inner class, so it extends A 

     @Override 
     protected void doPrepare() { 
      System.out.println("doPrepare"); 
     } 

     @Override 
     protected void doWork() { 
      System.out.println("doWork"); 
     } 
    }; 

    @Override 
    public void work() { // delegate implementation to descendant from A 
     this.wrapped.work(); 
    } 
} 

इस तरह, संरक्षित तरीकों एक गुमनाम आंतरिक वर्ग है, जो B के एक निजी अंतिम सदस्य है कि A तक फैली हुई है में छिपा रहता है। कुंजी यह है कि BA को विस्तारित नहीं करता है, लेकिन गुमनाम आंतरिक कक्षा में प्रतिनिधि द्वारा Worker इंटरफ़ेस की work() विधि लागू करता है।तो, यहां तक ​​कि मामले में भी B की work() विधि उसी वर्ग से संबंधित कक्षा से लागू की जा रही है, संरक्षित विधियां दिखाई नहीं देगी।

B b = new B(); 
b.work(); // this method is accessible via the Work interface 

आउटपुट:

doPrepare 
doWork 
+0

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

3

आप वास्तव में टेम्पलेट पैटर्न का उपयोग कर रहे हैं। प्रतिबिंब के साथ, इन कर सकते हैं इन सुरक्षाओं अतीत में उनके रास्ते डराने-धमकाने के लिए निर्धारित किया हैकर्स की

    Access Levels 
Modifier | Class | Package | Subclass | World | 
-------------------------------------------------- 
public  | Y  | Y  | Y  | Y  | 
protected | Y  | Y  | Y  | N  | 
no modifier | Y  | Y  | N  | N  | 
private  | Y  | N  | N  | N  | 

लघु:

1) अपने access modifiers को समझें: दो मुख्य बातें आप बाहर के उपयोगकर्ताओं से करने के लिए "छिपाएँ" विवरण कर सकता हैं अनौपचारिक रूप से कॉलिंग विधियों से आकस्मिक कोडर रखें जो सर्वोत्तम तरीके से उनके रास्ते से बाहर रखा जाता है। कोडर इसकी सराहना करेंगे। कोई भी एक अव्यवस्थित एपीआई का उपयोग करना चाहता है।

वर्तमान में b.doWork()protected है। तो यह केवल main() में दिखाई देगा यदि आपका main() कक्षा A (यह नहीं है) में है, subclass B (यह नहीं है), या उसी पैकेज में (स्पष्ट नहीं है, आपने पैकेज के बारे में कोई संकेत नहीं पोस्ट किया है) आपका कोड अंदर रहता है)।

यह सवाल पूछता है, आप b.doWork() को देखने से बचाने की कोशिश कर रहे हैं? यदि आप जो पैकेज बना रहे हैं वह केवल कुछ है जिसे आप विकसित कर रहे हैं तो यह इतना बुरा नहीं हो सकता है। यदि कई लोग इस पैकेज में कई अन्य वर्गों के साथ गड़बड़ कर रहे हैं तो यह संभावना नहीं है कि वे A और B कक्षाओं से घनिष्ठ परिचित होंगे। यह वह मामला है जहां आप सब कुछ लटकाना नहीं चाहते हैं जहां हर कोई इसे देख सके। यहां कक्षा और उप-वर्ग पहुंच की अनुमति केवल अच्छा होगा। लेकिन कोई संशोधक नहीं है जो पैकेज एक्सेस की अनुमति के बिना सबक्लास एक्सेस की अनुमति देता है। जब तक आप protected उप-वर्ग कर रहे हैं, तब तक आप सबसे अच्छा कर सकते हैं। इसे ध्यान में रखते हुए, बड़ी संख्या में कक्षाओं के साथ पैकेज बनाने के लिए मत जाओ। पैकेज को तंग और केंद्रित रखें। इस तरह अधिकांश लोग पैकेज के बाहर काम करेंगे जहां चीजें अच्छी और सरल लगती हैं।

संक्षेप में, यदि आप main() को पर कॉल करने से रोके main() को एक अलग पैकेज में डालने से रोकना चाहते हैं।

package some.other.package 

public final class Main { 
... 
} 

2) सलाह के इस अगले टुकड़ा के मूल्य अपने वर्तमान कोड के साथ समझने के लिए है क्योंकि यह मुद्दों है कि केवल ऊपर आ जाएगा, तो अपने कोड पर विस्तार किया जाता है को हल करने के बारे में है मुश्किल हो जाएगा।

public static void main(String[] args) { 
    A someALikeThingy = new B(); // <-- note use of type A 
    someALikeThingy.work();  //The name is silly but shows we've forgotten about B 
           //and know it isn't really just an A :) 
} 

: लेकिन यह वास्तव में बारीकी से b.doWork()

What does "program to interfaces, not implementations" mean?

की तरह देख रही बातों से लोगों की रक्षा अपने कोड में इसका क्या मतलब है यह बेहतर होगा कि मुख्य इस तरह दिख रही है के विचार से जुड़ा हुआ है क्यूं कर? बनाम B प्रकार का उपयोग कर क्या उपयोग करता है? खैर Aइंटरफ़ेस होने के करीब है। नोट, यहां मेरा मतलब यह नहीं है कि जब आप कीवर्ड interface का उपयोग करते हैं तो आपको क्या मिलता है।मेरा मतलब है कि B टाइप A का एक ठोस कार्यान्वयन है और यदि मुझे B के खिलाफ कोड लिखना नहीं है तो मैं वास्तव में नहीं जानता क्योंकि कौन जानता है कि अजीब गैर A चीजें B है। यहां समझना मुश्किल है क्योंकि मुख्य में कोड & मीठा छोटा है। इसके अलावा, इंटरफेसA और B सभी अलग नहीं हैं। उनके पास केवल एक सार्वजनिक विधि work() है। कल्पना करें कि main() लंबा और दृढ़ था, कल्पना कीजिए B में सभी प्रकार के गैर A विधियां लटक रही थीं। अब कल्पना करें कि आपको कक्षा C बनाने की आवश्यकता है जिसे आप मुख्य रूप से निपटाना चाहते हैं। यह देखने में सांत्वना नहीं होगी कि मुख्य को B खिलाया गया था, जहां तक ​​मुख्य रूप से प्रत्येक पंक्ति को पता है कि B कुछ साधारण A चीज़ की तरह है। new के बाद भाग के अलावा यह सच हो सकता है और आपको main() पर कुछ भी करने से बचा सकता है लेकिन new अपडेट कर रहा है। दरअसल, ऐसा नहीं है कि दृढ़ता से टाइप की गई भाषा का उपयोग क्यों कभी-कभी अच्छा हो सकता है?

< शेख़ी >
तुम भी B() साथ new के बाद वह हिस्सा अद्यतन करने लगता है कि अगर बहुत ज्यादा काम तुम अकेले नहीं हो रहा है। उस विशेष छोटे जुनून ने हमें dependency injection movement दिया है जो ढांचे का एक गुच्छा पैदा करता है जो एक साधारण इंजेक्शन की तुलना में ऑब्जेक्ट निर्माण को स्वचालित करने के बारे में वास्तव में अधिक था जो कि कोई भी कन्स्ट्रक्टर आपको करने दे सकता है।
</शेख़ी >

अद्यतन:

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

यह काउंटर अंतर्ज्ञानी प्रतीत होगा क्योंकि अब doWork() सार्वजनिक होना चाहिए। वह किस तरह की सुरक्षा है? अब आप B पूरी तरह से पीछे या यहां तक ​​कि AHandler के अंदर भी छिपा सकते हैं। यह कुछ मानव अनुकूल मुखौटा वर्ग है जो आपके टेम्पलेट कोड का उपयोग करता है लेकिन केवल work() का खुलासा करता है। A सिर्फ interface हो सकता है ताकि AHandler अभी भी पता नहीं है कि इसमें B है। यह दृष्टिकोण निर्भरता इंजेक्शन भीड़ के साथ लोकप्रिय है लेकिन निश्चित रूप से सार्वभौमिक अपील नहीं है। कुछ लोग इसे हर बार अंधा करते हैं जो कई को परेशान करता है। आप यहाँ इसके बारे में अधिक पढ़ सकते हैं: Prefer composition over inheritance?

+0

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

+0

2) इंटरफ़ेस दृष्टिकोण के लिए, इसमें दो डाउनसाइड्स हैं। सबसे पहले, यह जेनेरिक के साथ अच्छी तरह से काम नहीं करता है (मैं एक उदाहरण प्रदान कर सकता हूं)। दूसरा, यह आपको लिखने से रोकता है: 'परिणाम आर = नया बी (तर्क)। कार्य();', जिसे मैं कुछ हद तक मजबूर 'ए = = नया बी (तर्क) से बेहतर समझता हूं; परिणाम आर = ए। कार्य(); '। यह फिर से मैंने कुछ कोशिश की है और अभी भी कुछ बेहतर खोज रही है, हालांकि यह कुछ के लिए स्वीकार्य हो सकती है। –

+0

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

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