在我正在使用的第三方库中,有以下几个层次结构:
public abstract class Foo
{
// Class is public in the Java sense and public in the library's API
}
public class FooImpl extends Foo
{
// While this class is public in the Java sense, it is
// NOT part of the public API and is not even documented
// Library functions that hand back instances of this class
// hand them back as type Foo
}
库中的各种方法对Foo
类型的事物进行操作。 Foo
文档说用户可以扩展Foo
以进行具体实现,并且(显然)库提供了具体的实现。
我很满意库提供的实现,除了一个方法,我想改变它的行为。但是我提供的等级制度让我感到沮丧。
如果 Foo
是一个界面(它不是!),我只会这样做:
public FooWrapper implements Foo
{
private Foo wrappedImpl;
public FooWrapper(Foo toBeWrapped) {
wrappedImpl = toBeWrapped;
}
List<Whatever> methodIWantToTweak() {
List<Whatever> list = wrappedImpl.methodIWantToTweak();
do_something_to_list(list);
return list;
}
Something methodThatsOk() {
return wrappedImpl.methodThatsOk();
}
// similar delegations for remaining methods
}
但是因为它不一个接口我不能这样做,或者至少不能干净利落地做。
我能想到的唯一事情是:
1)扩展API非公共类FooImpl并覆盖我想要调整的方法。
2)做类似的事情:
public class FooWrapper extends Foo
{
private Foo wrappedImpl;
public FooWrapper(Foo toBeWrapped) {
super();
wrappedImpl = toBeWrapped;
}
List<Whatever> methodIWantToTweak() {
List<Whatever> list = wrappedImpl.methodIWantToTweak();
do_something_to_list(list);
return list;
}
Something methodThatsOk() {
return wrappedImpl.methodThatsOk();
}
// Override all non-final public, protected, and package-local
// methods and delegate them to wrappedImpl
}
这两种方法都闻起来像我。第一个问题是它依赖于一个它不应该知道的类,以及哪个类可以在库的未来版本中更改/消失/重命名。第二个问题是FooWrapper
的超类部分实际上没有被使用,并且不可能覆盖Foo
的任何最终或私有方法(这将导致问题,因为在那些情况下,一个人无法委托给wrappedImpl
)。
我想我必须采用第一种方法,因为至少它会给出正确的行为,而第二种可能会以潜在的邪恶,微妙的方式被打破,具体取决于Foo
的内部细节
那我是不是运气不好?我还有哪些其他方法/想法?
答案 0 :(得分:3)
如果您不需要覆盖任何Foo
的最终或受保护(见下文)方法,我会采用第二种方法:将Foo
视为接口并执行操作在那种情况下你会做的。优点是您不依赖于可以更改的FooImpl
。
final
的{{1}}和private
方法不是问题,因为您无法覆盖Foo
或FooImpl
是{接口,无论如何。另外,我认为您的意思是Foo
而不是protected
。
如果您 需要覆盖private
方法,则需要采用第一种方法。
答案 1 :(得分:2)
我想不出任何其他不会变得更臭的方法! (反思会起作用,但在这种情况下,它会比你试图避免的问题更糟糕。)
我认为第二种方法是最好的。如果Foo
抽象类被设计为公共类,那么它不应该有令人讨厌的内部细节可能会让你失望。如果是这样,他们的设计就错了。
请注意,因为您要为“真正的”Foo实例创建包装类:
您可以忽略抽象类为“真正的”Foo子类提供的大多数功能。
您可以忽略由Foo级属性表示的任何状态。
您可以在API中存根不需要在应用程序中使用的方法;例如将它们编码为UnsupportedOperationException
。
所有这些都有助于降低问题的复杂性。