Java、多态性、静态类型和;“查询接口”;图案
我正在做一个API设计,关于Java和多态性,我到现在都没有想到。如果我创建这样的API:Java、多态性、静态类型和;“查询接口”;图案,java,oop,polymorphism,api-design,Java,Oop,Polymorphism,Api Design,我正在做一个API设计,关于Java和多态性,我到现在都没有想到。如果我创建这样的API: interface FooFactory { public Foo getFoo(); } interface Foo { public void sayFoo(); } interface EnhancedFoo extends Foo { public void patHeadAndRubBelly(); } class EnhancedFooImpl implement
interface FooFactory
{
public Foo getFoo();
}
interface Foo
{
public void sayFoo();
}
interface EnhancedFoo extends Foo
{
public void patHeadAndRubBelly();
}
class EnhancedFooImpl implements EnhancedFoo
{
... implementation here ...
}
class EnhancedFooFactoryImpl implements FooFactory
{
@Override public EnhancedFoo getFoo() { return new EnhancedFooImpl(); }
}
那么我的foodfactory
实现唯一可以依赖的就是Foo
实现。如果我决定提供一些增强的方法,例如:
interface FooFactory
{
public Foo getFoo();
}
interface Foo
{
public void sayFoo();
}
interface EnhancedFoo extends Foo
{
public void patHeadAndRubBelly();
}
class EnhancedFooImpl implements EnhancedFoo
{
... implementation here ...
}
class EnhancedFooFactoryImpl implements FooFactory
{
@Override public EnhancedFoo getFoo() { return new EnhancedFooImpl(); }
}
我的API客户机使用EnhancedFoo
接口的唯一方法是获得Foo
接口并尝试将其转换为EnhancedFoo
我记得Microsoft在界面中处理COM的方式:
HRESULT QueryInterface(
[in] REFIID riid,
[out] void **ppvObject
);
这样做的目的是为所需的接口传入一个GUID,如果成功,则返回一个指针,以确保可以安全地将其强制转换到所需的接口
我可以用Java以类型安全的方式做类似的事情:
interface FooFactory
{
public <T extends Foo> T getFoo(Class<T> fooClass);
}
接口工厂
{
公共T getFoo(类fooClass);
}
其中,我提供了一个应请求实现,该实现要么返回所需子接口的实例,要么在没有可用子接口的情况下返回null
我的问题是:
- 查询接口模式是否合理
- 我应该只用铸造吗
- 或者,处理API中多态性的唯一正确方法是限制客户端严格使用所讨论的普通方法吗?(例如,我的示例中
中的方法,而不是任何Foo
方法)EnhancedFoo
澄清:客户端代码不会知道工厂实现。(假设它使用依赖注入或一些服务提供商架构,如java的
ServiceLoader
或NetBeansLookup
),因此作为客户机,我不知道有什么可用。工厂可能有权访问多个Foo
衍生产品,我希望客户端能够请求它想要的功能集,要么它得到了,要么它将不得不依赖基本Foo
功能
我想对我来说最困难的部分是这里有运行时依赖性。。。纯静态类型安全方法,其中所有内容在编译时都是固定的,这意味着我只能依赖基本的
Foo
功能。这种方法对我来说很熟悉,但随后我就失去了可能的增强功能。虽然更具动态性/机会主义的方法可以利用这些功能,但我不确定构建一个使用它的系统的正确方法。您可以简单地用泛型声明您的工厂,其余的保持原样:
static interface FooFactory {
public <T extends Foo> T getFoo();
}
(这在Java 8之前可能不起作用)
如果
foodfactory
不是您所期望的,则行EnhancedFoo e=f.getFoo()
将抛出一个ClassCastException
为什么不参数化工厂界面
static interface FooFactory<T extends Foo> {
public T getFoo();
}
如果某些Foo实现需要构造函数参数,您可以始终将相应的If放入foctory方法中,并“手动”实例化对象:
@覆盖
公共T getFoo(c类){
if(someParameterizedFoo.class.equals(c)){
SomeParamtrizedFoo spf=新的SomeParamtrizedFoo(“constr arg”);
spf.setParam(16136);
返回(T)spf;
}
试一试{
返回c.newInstance();
}捕捉(反射操作异常e){
返回null;
}
}
类型推断在Java8.Hmm中得到了改进。。。异常方法对于Python来说是正常的,但对于Java来说似乎是不合适的。我认为它非常合适——比空检查更好。我想我的想法是,客户机可能希望使用一些特定的功能。Foo
类可能有多个扩展,所以我不想把它留给工厂;相反,这应该由客户决定。我在API设计方面没有足够的经验,所以我不能100%确定如何精确地形成我的问题。在编辑中添加了一个澄清说明:用c.isAssignableFrom(someparametriczedfoo.class)
替换someparametriczedfoo.class.equals(c)
,我也会投票支持isAssignableFrom
;我不关心具体哪个类是实现类;我确实关心它是否实现/扩展了类c
,这正是客户机所要求的。如果我想使用这个框架,那些实现c
的具体类将提供一个兼容的构造函数。。。事实上,现在我想起来了,答案中的代码有点可疑。factory的特定实例应该只生成一个类的实例。泛型只是为了类型安全,而不是告诉工厂生产什么类@Tomasz Gawel,在您的代码中,工厂将生成您想要传入的任何类,只要它有一个默认构造函数。
FooFactory<EnhancedFoo> f1 = new EnhancedFooFactoryImpl();
EnhancedFoo foo = f1.getFoo();
FooFactory<?> f2 = new EnhancedFooFactoryImpl();
Foo foo = f2.getFoo();
interface FooFactory {
public <T extends Foo> T getFoo(Class<T> fooClass);
}
class FooFactoryImpl implements FooFactory {
@Override
public <T extends Foo> T getFoo(Class<T> c) {
try {
return c.newInstance();
} catch (ReflectiveOperationException e) {
return null;
}
}
}
FooFactory ff = new FooFactoryImpl();
EnhancedFoo ef = ff.getFoo(EnhancedFoo.class);
Foo f = ff.getFoo(Foo.class);
@Override
public <T extends Foo> T getFoo(Class<T> c) {
if(SomeParametrizedFoo.class.equals(c)) {
SomeParamtrizedFoo spf = new SomeParamtrizedFoo("constr arg");
spf.setParam(16136);
return (T) spf;
}
try {
return c.newInstance();
} catch (ReflectiveOperationException e) {
return null;
}
}