【问题标题】:Choose which subclass to extend from at runtime in java在java中选择在运行时从哪个子类扩展
【发布时间】:2015-07-27 07:06:13
【问题描述】:

我有两个类(比如 B 和 C),它们都派生自一个类(比如 A)。现在我需要编写一个类(比如 D),它应该在运行时从 B 或 C 动态派生。

B、C 和 A 是通过库提供给我的类,我控制的唯一类是 D 类。关于上述约束,我如何写 D。显然 B 和 C 来自不同的供应商,因此需要在 D 中覆盖不同的方法,具体取决于它的父类。

我不能编写从 B 和 C 子类化的不同版本的 D,因为覆盖方法在不同的方法名称下具有相同的代码。

【问题讨论】:

标签: java android oop design-patterns subclass


【解决方案1】:

你应该定义一个接口I,然后定义对应BC的具体实现。在运行时,您可以确定I 的哪个实现是必要的,可能使用工厂方法。那么你的代码只需要调用I的方法而不是BC的方法。

编辑

似乎对它的工作原理有些困惑。您想要的是让您的业务逻辑在属于您的一致、稳定的 API 上运行。为此,您创建一个我们称之为I 的接口。现在,您需要针对需要适应的不同外部类(ABC)的实现类。它可能看起来像这样:

public interface I {
    void doSomething();
}

public class IA implements I {
    private A a;

    public IA(A a) {
        this.a = a;
    }

    public void doSomething() {
        // specific to A
        a.doSomethingUnique();
    }
}

// similar implementation classes for B and C

现在您需要在运行时针对您当前的情况获取I 的实例。任何告诉您在运行时正在使用哪个特定类的信息都可以用于此目的。在最坏的情况下,您可以这样做:

// in some util class
public static I getI(Object obj) {
    if (obj instanceof A) {
        return new IA((A) obj);
    } else if (obj instanceof B) {
        return new IB((B) obj);
    } else if (obj instanceof C) {
        return new IC((C) obj);
    }
    // maybe throw an exception? or return a mock I implementation?
}

现在您的所有业务逻辑仅引用I 的实例并调用您接口中定义的方法,抽象出您无法控制的不同具体类。

【讨论】:

  • 宾果游戏。换句话说,将BC 放入(它们自己的)包装对象中,其类都实现I,然后使用包装类而不是直接访问BC
  • 我清楚地写了A,B和C根本不在我的控制之下。我的类目前派生自 A。现在我必须有选择地启用从 B 或 C 派生,并在不同的回调中执行相同的代码。
  • 这个解决方案完全不需要 A、B 或 C 在您的控制之下。这种模式被广泛用于在不修改其源代码的情况下将现有类(或多个类)的接口适配到另一个类。它被称为适配器模式,请在此处阅读:en.wikipedia.org/wiki/Adapter_pattern
  • 问题是A、B和C类都不是接口。
  • A、B 和 C 不必是接口。
【解决方案2】:

您可以通过私有继承来做到这一点。

如果你有:

class A
{
  public void methodA() {...}
}
class B extends A
{
  public void methodB() {...}
}
class C extends A
{
  public void methodC() {...}
}

D 将被实现为:

class D
{
  private B b;
  private C c;
  private D() {}
  public static D instantiateAsB()
  {
    D res = new D();
    res.b = new B();
  }
  public static D instantiateAsC()
  {
    D res = new D();
    res.c = new C();
  }
  public void methodA()
  {
    if ( b!=null )
      b.methodA();
    else
      c.methodA();
  }
  public foid methodB() 
  {
    if ( b==null )
      throw new MethodNotImplementedException();
    else
      b.methodB();
  }
  public foid methodC()
  {
    if ( c==null )
      throw new MethodNotImplementedException();
    else
      c.methodC();
  }
}

私有继承有一些缺点。一个是 D 不会是 A。所以你不能将 D 对象作为参数传递给需要 A 的方法。这段代码不会编译:

void method( A a ) {...}

D d = D.instantiateAsB();
method( d );

您可以使用 D 定义中的强制转换方法来解决这个问题:

// inside D class :
public A castAsA()
{
  if ( b!=null )
    return (A)b;
  else
    return (A)c;
}

public B castAsB()
{
  if ( b==null )
    throw new ClassCastException();
  else
    return b;
}

public C castAsC()
{
  if ( c==null )
    throw new ClassCastException();
  else
    return c;
}

而之前的非编译代码将被改写为:

D d = D.instantiateAsB()
method( d.castAsA() );

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-10-14
    • 2010-11-03
    • 1970-01-01
    • 2016-07-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多