我有两个类(比如B& C),它们都来自一个类(比如说A)。现在我需要编写一个类(比如D),它应该在运行时从B或C动态派生。
B,C& A是通过库提供给我的类,我控制的唯一类是D类。如何针对上述约束编写D.显然B和C来自不同的供应商,因此需要在D中覆盖不同的方法,具体取决于它的父类。
我不能写出不同版本的D,它们将从B& C,因为覆盖方法在不同的方法名下具有相同的代码。
答案 0 :(得分:4)
您应该定义接口I
,然后定义与B
和C
对应的具体实现。在运行时,您可以使用工厂方法确定I
的哪个实现是必需的。那么您的代码只需调用I
的方法,而不是B
或C
的方法。
修改强>
似乎对这是如何工作存在一些困惑。您想要的是您的业务逻辑在属于您的一致,稳定的API上运行。为此,您创建了一个我们称之为I
的界面。现在,您需要适应(A
,B
和C
)所需的不同外部类的实现类。它可能看起来像这样:
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() );