Java,多态,静态类型和" QueryInterface"图案

时间:2014-11-13 14:51:24

标签: java oop polymorphism api-design

我正在从事API设计工作,而且到目前为止我还没有考虑过Java和多态的问题。如果我创建这样的API:

interface FooFactory
{
    public Foo getFoo();
}

interface Foo
{
    public void sayFoo();
}

然后我唯一能够依赖FooFactory实现的是Foo实现。如果我决定提供一些增强的方法,例如:

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在IUnknown界面中处理COM的方式:

HRESULT QueryInterface(
   [in]   REFIID riid,
   [out]  void **ppvObject
);

这个想法是你传入你想要的接口的GUID,如果成功,你会得到一个指针,保证你可以安全地将它转换到你想要的接口。

我可以使用Java以类型安全的方式做类似的事情:

interface FooFactory
{
    public <T extends Foo> T getFoo(Class<T> fooClass);
}

我在请求时提供一个实现,它返回一个所需子接口的实例,如果没有可用的话,返回null。

我的问题是:

  • 是QueryInterface模式合理吗?
  • 我应该使用铸造吗?
  • 或者是唯一正确处理API中的多态性以限制客户端严格使用相关普通方法的方法吗? (例如,我的示例中的Foo中的方法,而不是任何EnhancedFoo方法)

澄清:工厂实施不会被客户端代码所知。 (让我们说它使用依赖注入或某些服务提供商架构,如java ServiceLoader或NetBeans Lookup。)所以作为客户,我不知道&# 39;可用。工厂可能有多个Foo衍生产品的访问权限,我希望客户端能够请求它想要的功能集,并且要么得到它,要么它必须回退到基础{{1}功能。

我认为对我来说困难的部分是这里存在运行时依赖性...纯静态类型安全方法,其中所有内容都在编译时修复,这意味着我只能依赖于基本的Foo功能。这种方法对我来说很熟悉,但后来我失去了可能的增强功能。虽然更动态/机会主义的方法可以利用这些功能,但我不确定构建使用它的系统的正确方法。

2 个答案:

答案 0 :(得分:1)

您可以简单地使用泛型声明您的工厂,并按原样保留其余部分:

static interface FooFactory {
  public <T extends Foo> T getFoo();
}

然后感谢类型推断,这将编译:

FooFactory f = new EnhancedFooFactoryImpl();
EnhancedFoo e = f.getFoo();

(这可能在Java 8之前不起作用)

如果FooFactory不符合您的预期,则行EnhancedFoo e = f.getFoo();会抛出ClassCastException

答案 1 :(得分:1)

为什么不参数化工厂界面?

static interface FooFactory<T extends Foo> {
    public T getFoo();
}

然后:

class EnhancedFooFactoryImpl implements FooFactory<EnhancedFoo> {

    @Override 
    public EnhancedFoo getFoo() { return new EnhancedFooImpl(); }
}

实例化:

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

这有两个方面的优势: 1.您可以控制用户想要创建哪些实际分类。 2.拥有一个类对象,你可以用反射来实例化它。

因此,在这种情况下,您不必使用特殊的Factory类来增强foo:

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

如果某些Foo实现需要构造函数参数,则可以始终在foctory方法中放置相应的if并“手动”实例化对象:

    @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;
        }
    }