选择在运行时从java扩展的子类

时间:2015-07-27 07:06:14

标签: java android oop design-patterns subclass

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

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

我不能写出不同版本的D,它们将从B& C,因为覆盖方法在不同的方法名下具有相同的代码。

2 个答案:

答案 0 :(得分:4)

您应该定义接口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的实例,并调用接口中定义的方法,抽象出您无法控制的不同具体类。

答案 1 :(得分:0)

你可以使用私有继承来完成它。

如果你有:

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() );