Java(Android)中多重继承的正确解决方法


15

我有一个概念上的问题,即代码的正确实现似乎需要多重继承,在许多OO语言中这不是问题,但是由于该项目是针对Android的,所以没有诸如multi之类的东西extends

我有一堆的活动,从不同的基类,如简单的衍生ActivityTabActivityListActivityExpandableListActivity,等。此外我有我需要的地方变成一些代码片段onStartonStoponSaveInstanceStateonRestoreInstanceState和其他标准的事件中的所有活动处理器。

如果所有活动只有一个基类,则可以将代码放入一个特殊的中间派生类中,然后创建将其扩展的所有活动。不幸的是,事实并非如此,因为存在多个基类。但是,将代码的相同部分放入几个中间类并不是一种方法,恕我直言。

另一种方法可以是创建一个助手对象,并将上述事件的所有调用委托给助手。但这要求包括辅助对象,并在所有中间类中重新定义所有处理程序。因此,这里的第一种方法没有太大区别-仍然有很多代码重复。

如果在Windows下发生类似情况,我将继承基类(与ActivityAndroid中的类“相对应”的基类),并在那里(在一个地方)捕获适当的消息。

为此,在Java / Android中可以做什么?我知道有一些有趣的工具,例如Java工具带有一些真实的示例),但是我不是Java专家,所以不确定在这种情况下是否值得尝试。

如果我错过了其他一些不错的解决方案,请提及它们。

更新:

对于那些可能对解决Android中的相同问题感兴趣的人,我发现了一个简单的解决方法。存在Application类,该类除其他外提供接口ActivityLifecycleCallbacks。它正是我所需要的,它使我们能够拦截所有活动的重要事件并为其添加一些价值。此方法的唯一缺点是,它从API级别14开始就可用,这在许多情况下还远远不够(目前对API级别10的支持已成为典型要求)。

Answers:


6

恐怕没有android / java中的代码复制,您就无法实现您的类系统。

但是,如果将特殊的中间派生类复合帮助器对象结合使用,则可以最大程度地减少代码重复。这称为Decorator_pattern

    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。这是不得已的做法,尽管实际上演示了我希望避免的事情-代码重复。如果没有其他有趣的想法,我会接受您的回答。我可以使用泛型来概括“中间体”的代码吗?
Stan

6

我认为您正在尝试避免错误类型的代码重复。我相信Michael Feathers撰写了有关此文章,但不幸的是我找不到它。他描述的方式是,您可以将代码想像成具有橙色部分的两个部分:外皮和果肉。外皮是诸如方法声明,字段声明,类声明等之类的东西。实施。

谈到干,您要避免重复纸浆。但是通常在此过程中您会创建更多的外皮。没关系。

这是一个例子:

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()签名(即:外皮)。operation()每个类的实现(浆)应有所不同。我认为您的代码将最终使代码更整洁,并且减少纸浆重复。


3

您应该更喜欢组合而不是继承。一个很好的例子是.NET WCF框架中的IExtension“ 模式 ”。从总体上讲,您有3个接口,即IExtension,IExtensibleObject和IExtensionCollection。然后,您可以通过将IExtension实例的addig IExtension实例添加到其Extension IExtensionCollection属性来与IExtensibleObject对象组成不同的行为。在Java中,它看起来应该像这样,但是不必创建自己的IExtensioncollection实现,该实现在添加/删除项目时调用attach和detach方法。还要注意,由您决定是否在可扩展类中定义扩展点。该示例使用类似事件的回调机制:

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,但是我发现这个答案很难理解。您可以添加使用这三个界面的示例吗?
Daniel Kaplan

谢谢,非常有趣。但是,仅在扩展对象中注册扩展的回调会不会更简单?像processor.addHandler(console)提供的那样ConsoleProcessor可以实现回调接口本身。在“扩展”模式看起来像一个mixin visitordecorator,但是否有必要在这种情况下?
斯坦(Stan)

如果您直接在Processor(== ExtensibleObject)中注册扩展,则您会遇到麻烦。这里的想法是拥有可以在具有相同扩展点的可扩展对象之间重用的扩展。实际上,模式就像混合模式模拟一样。
m0sa

嗯,我不确定按接口绑定是否紧密耦合。最有趣的是,即使在扩展模式既扩展的对象和扩展依靠同一个工作界面(“回调”),所以耦合仍然是相同的,恕我直言。换句话说,如果没有在“处理器”中支持工作者接口的编码,我将无法将现有扩展插入新的可扩展对象中。如果“扩展点”实际上是指工作人员界面,那么我认为没有什么不同。
斯坦(Stan)

0

从Android 3.0开始,可以使用Fragment优雅地解决此问题。片段具有自己的生命周期回调,可以放置在Activity中。我不确定这是否适用于所有事件。

我也不确定的另一种选择(缺乏深入的Android专业知识)可能是以k3b建议的相反方式使用Decorator:创建一个ActivityWrapper其回调方法包含您的通用代码,然后转发给包装的Activity对象(您的实际的实现类),然后让Android启动该包装器。


-3

的确,Java不允许多重继承,但是您可以通过使SomeActivity子类中的每个子类扩展原始Activity类来或多或少地模拟它。

您将看到类似以下内容的内容:

public class TabActivity extends Activity {
    .
    .
    .
}

2
这是类已经做的事情,但是Activity基类是Android API的一部分,开发人员无法更改。
Michael Borgwardt
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.