जावा (Android) में कई उत्तराधिकार के लिए उचित समाधान


15

मुझे कोड के उचित कार्यान्वयन के साथ एक वैचारिक समस्या है, जिसके लिए कई विरासत की आवश्यकता लगती है, जो कई ओओ भाषाओं में समस्या नहीं होगी, लेकिन जैसा कि परियोजना एंड्रॉइड के लिए है, इसमें मल्टीपल जैसी कोई चीज नहीं है extends

मैं गतिविधियों का एक गुच्छा, इस तरह के सरल रूप में विभिन्न आधार वर्ग, से प्राप्त होता है Activity, TabActivity, ListActivity, ExpandableListActivity, आदि इसके अलावा, मैं कुछ कोड के टुकड़े जो मैं में जगह की जरूरत है onStart, onStop, onSaveInstanceState, onRestoreInstanceStateऔर अन्य मानक घटना सभी गतिविधियों में संचालकों।

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

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

अगर विंडोज के तहत ऐसी ही स्थिति उत्पन्न हुई, तो मैं बेस क्लास ( Activityएंड्रॉइड में क्लास के लिए "मेल खाती है" ) और वहां (एक ही स्थान पर) उचित संदेश ट्रैप कर सकता हूं ।

इसके लिए जावा / एंड्रॉइड में क्या किया जा सकता है? मुझे पता है कि जावा इंस्ट्रूमेंटेशन ( कुछ वास्तविक उदाहरणों के साथ ) जैसे दिलचस्प उपकरण हैं , लेकिन मैं एक जावा गुरु नहीं हूं, और यह निश्चित नहीं है कि इस विशिष्ट मामले में प्रयास करने के लायक है या नहीं।

अगर मुझे कुछ अन्य सभ्य समाधान याद आए, तो कृपया उनका उल्लेख करें।

अपडेट करें:

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

जवाबों:


6

मुझे डर है कि आप एंड्रॉइड / जावा में कोडेड्यूलेशन के बिना अपने क्लास सिस्टम को लागू नहीं कर सकते।

हालाँकि, यदि आप विशेष मध्यवर्ती व्युत्पन्न वर्ग को समग्र सहायक वस्तु के साथ जोड़ते हैं तो आप कोडेडुप्लीकेशन को कम कर सकते हैं । इसे डेकोरेटर_पैटर्न कहा जाता है :

    class ActivityHelper {
        Activity owner;
        public ActivityHelper(Activity owner){/*...*/}
        onStart(/*...*/){/*...*/}   
    }

    public class MyTabActivityBase extends TabActivity {
        private ActivityHelper helper;
        public MyTabActivityBase(/*...*/) {
            this.helper = new ActivityHelper(this);
        }

        protected void onStart() {
            super.onStart();
            this.helper.onStart();
        }
        // the same for onStop, onSaveInstanceState, onRestoreInstanceState,...
    }

    Public class MySpecialTabActivity extends MyTabActivityBase  {
       // non helper logic goes here ....
    }

इसलिए हर बेस क्लास में आप एक इंटरमीडिएट बेसकैप बनाते हैं जो हेल्पर को अपनी कॉल दर्शाता है। मध्यवर्ती बेसकेसक्लेस बेसकलेज़ को छोड़कर समान हैं जहां से उन्हें विरासत में मिला है।


1
हाँ धन्यवाद। मुझे पता है decordator pattern। यह एक अंतिम उपाय है, जो वास्तव में दर्शाता है कि मैं क्या करना चाहता हूं - कोड दोहराव। अगर कोई अन्य विचारों को प्रेरित करता है तो मैं आपका उत्तर स्वीकार करूंगा। क्या मैं संभवतः "मध्यवर्ती" के कोड को सामान्य करने के लिए जेनरिक का उपयोग कर सकता हूं?
स्टेन

6

मुझे लगता है कि आप गलत प्रकार के कोड दोहराव से बचने की कोशिश कर रहे हैं। मेरा मानना ​​है कि माइकल फेदर्स ने इस बारे में एक लेख लिखा था लेकिन दुर्भाग्य से मैं इसे नहीं पा सका। जिस तरह से वह इसका वर्णन करता है वह यह है कि आप अपने कोड के बारे में सोच सकते हैं जैसे कि संतरे के दो हिस्से: द रिंड और पल्प। छंद सामान है जैसे विधि घोषणाएं, क्षेत्र घोषणाएं, वर्ग घोषणाएं, आदि लुगदी इन विधियों के अंदर सामान है; कार्यान्वयन।

जब DRY की बात आती है, तो आप लुगदी की नकल से बचना चाहते हैं । लेकिन अक्सर इस प्रक्रिया में आप अधिक विचार पैदा करते हैं। और यह ठीक है।

यहाँ एक उदाहरण है:

public void method() { //rind
    boolean foundSword = false;
    for (Item item : items)
        if (item instanceof Sword)
             foundSword = true;
    boolean foundShield = false;
    for (Item item : items)
        if (item instanceof Shield)
             founShield = true;
    if (foundSword && foundShield)
        //...
}  //rind

इसे इस में बदला जा सकता है:

public void method() {  //rind
    if (foundSword(items) && foundShield(items))
        //...
} //rind

public boolean foundSword(items) { //rind
    return containsItemType(items, Sword.class);
} //rind

public boolean foundShield(items) { //rind
    return containsItemType(items, Shield.class);
} //rind

public boolean containsItemType(items, Class<Item> itemClass) { //rind
    for (Item item : items)
        if (item.getClass() == itemClass)
             return true;
    return false;
} //rind

हमने इस रीफैक्टरिंग में बहुत सारे जोड़ दिए। लेकिन, दूसरे उदाहरण में method()कम DRY उल्लंघन के साथ बहुत अधिक क्लीनर है ।

आपने कहा कि आप डेकोरेटर पैटर्न से बचना चाहते हैं क्योंकि इससे कोड दोहराव होता है। यदि आप उस लिंक में छवि को देखते हैं तो आप देखेंगे कि यह केवल operation()हस्ताक्षर की नकल करेगा (यानी: rind)। operation()कार्यान्वयन (गूदा) प्रत्येक वर्ग के लिए अलग-अलग होना चाहिए। मुझे लगता है कि आपके कोड के परिणामस्वरूप क्लीनर समाप्त हो जाएगा और कम लुगदी दोहराव होगा।


3

आपको वंशानुक्रम पर रचना पसंद करनी चाहिए। एक अच्छा उदाहरण .NET WCF ढांचे में IExtension " पैटर्न " है। बैसिकली आपके पास 3 इंटरफेस, IExtension, IExtensibleObject और IExtensionCollection हैं। फिर आप इसे IE IETensionCollection संपत्ति का विस्तार करने के लिए addig IExtension उदाहरणों द्वारा एक IExtensibleObject वस्तु के साथ विभिन्न व्यवहारों की रचना कर सकते हैं। जावा में यह कुछ इस तरह दिखना चाहिए, हालांकि यह नहीं कि आपको अपना IExtensioncollection कार्यान्वयन बनाना है जो कि आइटमों को जोड़े / हटाए जाने पर संलग्न और अलग करने के तरीकों को कॉल करता है। यह भी ध्यान दें कि यह आपके एक्स्टेंसिबल क्लास में विस्तार बिंदुओं को परिभाषित करने के लिए है। उदाहरण एक घटना की तरह कॉलबैक तंत्र का उपयोग करता है:

import java.util.*;

interface IExtensionCollection<T> extends List<IExtension<T>> {
    public T getOwner();
}

interface IExtensibleObject<T> {
    IExtensionCollection<T> getExtensions();
}

interface IExtension<T> {
    void attach(T target);
    void detach(T target);
}

class ExtensionCollection<T>
    extends LinkedList<IExtension<T>>
    implements IExtensionCollection<T> {

    private T owner;
    public ExtensionCollection(T owner) { this.owner = owner; }
    public T getOwner() { return owner; }
    public boolean add(IExtension<T> e) {
        boolean result = super.add(e);
        if(result) e.attach(owner);
        return result;
    }
    // TODO override remove handler
}

interface ProcessorCallback {
    void processing(byte[] data);
    void processed(byte[] data);
}

class Processor implements IExtensibleObject<Processor> {
    private ExtensionCollection<Processor> extensions;
    private Vector<ProcessorCallback> processorCallbacks;
    public Processor() {
        extensions = new ExtensionCollection<Processor>(this);
        processorCallbacks = new Vector<ProcessorCallback>();
    }
    public IExtensionCollection<Processor> getExtensions() { return extensions; }
    public void addHandler(ProcessorCallback cb) { processorCallbacks.add(cb); }
    public void removeHandler(ProcessorCallback cb) { processorCallbacks.remove(cb); }

    public void process(byte[] data) {
        onProcessing(data);
        // do the actual processing;
        onProcessed(data);
    }
    protected void onProcessing(byte[] data) {
        for(ProcessorCallback cb : processorCallbacks) cb.processing(data);
    }
    protected void onProcessed(byte[] data) {
        for(ProcessorCallback cb : processorCallbacks) cb.processed(data);
    }
}

class ConsoleProcessor implements IExtension<Processor> {
    public ProcessorCallback console = new ProcessorCallback() {
        public void processing(byte[] data) {

        }
        public void processed(byte[] data) {
            System.out.println("processed " + data.length + " bytes...");
        }
    };
    public void attach(Processor target) {
        target.addHandler(console);
    }
    public void detach(Processor target) {
        target.removeHandler(console);
    }
}

class Main {
    public static void main(String[] args) {
        Processor processor = new Processor();
        IExtension<Processor> console = new ConsoleProcessor();
        processor.getExtensions().add(console);

        processor.process(new byte[8]);
    }
}

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


1
शायद यह इसलिए है क्योंकि मैं .NET का उपयोग नहीं करता, लेकिन मुझे यह उत्तर समझने में बहुत कठिन लगा। क्या आप इन तीन इंटरफेस का उपयोग करने का एक उदाहरण जोड़ सकते हैं?
डेनियल कपलान

धन्यवाद, बहुत दिलचस्प। लेकिन क्या यह एक्स्टेंसिबल ऑब्जेक्ट में एक्सटेंशन के कॉलबैक को पंजीकृत करने के लिए बहुत सरल नहीं होगा? कुछ ऐसा processor.addHandler(console)प्रदान किया गया है जो ConsoleProcessorकॉलबैक इंटरफ़ेस को लागू करेगा। "एक्सटेंशन" पैटर्न मिक्सिन की तरह दिखता है visitorऔर decorator, लेकिन क्या इस मामले में यह आवश्यक है?
स्टेन

यदि आप प्रोसेसर में सीधे एक्सटेंशन दर्ज करते हैं (== एक्स्टेंसिबल ऑबजेक्ट) तो आपके पास कपलिंग है। यहां विचार के विस्तार के लिए है कि एक ही विस्तार बिंदु के साथ एक्स्टेंसिबल ऑब्जेक्ट्स के बीच पुन: उपयोग किया जा सकता है। वास्तव में पैटर्न एक तरह मोरा है mixin पैटर्न अनुकरण।
m0sa

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

0

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

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


-3

यह सच है कि जावा एकाधिक-वंशानुक्रम की अनुमति नहीं देता है, लेकिन आप इसे अपने प्रत्येक OneActivity उपवर्गों में से प्रत्येक को मूल गतिविधि वर्ग का विस्तार करके कम या अधिक अनुकरण कर सकते हैं।

आपके पास कुछ ऐसा होगा:

public class TabActivity extends Activity {
    .
    .
    .
}

2
यह वही है जो कक्षाएं पहले से करते हैं, लेकिन गतिविधि आधार वर्ग Android एपीआई का हिस्सा है और इसे डेवलपर्स द्वारा नहीं बदला जा सकता है।
माइकल बोर्गवर्ड
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.