通用接口

时间:2009-10-26 07:24:04

标签: java interface

假设我想定义一个代表远程服务调用的接口。现在,对远程服务的调用通常会返回一些内容,但也可能包含输入参数。假设实现类通常只实现一种服务方法。鉴于以上信息,以下是一个糟糕的设计(它感觉不对):

public interface IExecutesService<A,B>
{
    public A executeService();
    public A executeService(B inputParameter);
}

现在,假设我使用一个使用输入参数执行远程服务的类来实现此接口:

public class ServiceA implements IExecutesService<String,String>
{
  public String executeService()
  {
    //This service call should not be executed by this class
    throw new IllegalStateException("This method should not be called for this class...blabla");
  }

  public String executeService(String inputParameter)
  {
    //execute some service
  }

关于上述问题我有两个问题:

  1. 在您想要提供需要不同输入参数和接口方法返回类型的子类的情况下,是否使用通用接口(IExecutesService<A,B>)是否合适?
  2. 我怎样才能做到更好?即我想将我的服务执行者分组到一个公共接口(IExecutesService)下;但是,实现类通常只实现其中一个方法,并且使用IllegalStateException感觉非常难看。此外,IExecutesService<A,B>中的B类型参数对于调用没有任何输入参数的服务的实现类来说将是多余的。对于两个不同的服务调用来说,创建两个独立的接口似乎有些过分。

5 个答案:

答案 0 :(得分:84)

这是一个建议:

public interface Service<T,U> {
    T executeService(U... args);
}

public class MyService implements Service<String, Integer> {
    @Override
    public String executeService(Integer... args) {
        // do stuff
        return null;
    }
}

由于类型擦除,任何类只能实现其中一个。这至少消除了冗余方法。

这不是你提出的不合理的界面,但我不是百分之百确定它增加了什么价值。您可能只想使用标准Callable接口。它不支持参数,但接口的那部分值最小(imho)。

答案 1 :(得分:20)

这是另一个建议:

public interface Service<T> {
   T execute();
}

使用这个简单的界面你可以在具体的服务类中通过构造函数传递参数:

public class FooService implements Service<String> {

    private final String input1;
    private final int input2;

    public FooService(String input1, int input2) {
       this.input1 = input1;
       this.input2 = input2;
    }

    @Override
    public String execute() {
        return String.format("'%s%d'", input1, input2);
    }
}

答案 2 :(得分:9)

我会留下两个不同的界面。

你说'我想把我的服务执行者分组到一个公共接口上......对于两个不同的服务调用来说,创建两个独立的接口似乎有些过分......一个类只会实现其中一个接口“

目前尚不清楚拥有单一界面的原因是什么。如果您想将它用作标记,则可以改为使用注释。

另一点是,可能会出现需求更改的情况,并且界面上会出现带有其他签名的方法。当然可以使用Adapter模式,但是看到特定的类实现与三个方法的接口会很奇怪,其中两个方法都会抛出UnsupportedOperationException。第四种方法可能会出现等等。

答案 3 :(得分:4)

作为一个严格符合你的问题的答案,我支持cleytus的提议。


你也可以使用标记接口(没有方法),比如DistantCall,使用几个具有你想要的精确签名的子接口。

  • 如果您想为所有这些代码编写一些通用代码,通用界面将用于标记所有这些代码。
  • 使用cleytus的通用签名可以减少特定接口的数量。

“可重用”接口的示例:

    public interface DistantCall {
    }

    public interface TUDistantCall<T,U> extends DistantCall {
      T execute(U... us);
    }

    public interface UDistantCall<U> extends DistantCall {
      void execute(U... us);
    }

    public interface TDistantCall<T> extends DistantCall {
      T execute();
    }

    public interface TUVDistantCall<T, U, V> extends DistantCall {
      T execute(U u, V... vs);
    }
    ....

更新以回应OP评论

我没有考虑调用中的任何 instanceof 。我在想你的调用代码知道它正在调用什么,你只需要在一个通用接口中为一些通用代码组装几个远程调用(例如,审计所有远程调用,出于性能原因)。 在你的问题中,我没有看到调用代码是通用的:-(

如果是这样,我建议您只有一个界面,只有一个签名。有几个只会带来更多的复杂性,无所不能。

但是,你需要问自己一些更广泛的问题:
如何确保来电者和被叫者确实正确沟通?

这可能是对这个问题的后续行动,或者是另一个问题......

答案 4 :(得分:3)

如果我理解正确,你想让一个类实现多个具有不同输入/输出参数的接口?这在Java中不起作用,因为泛型是通过擦除来实现的。

Java泛型的问题在于泛型实际上只不过是编译魔术。在运行时,类不保留有关用于通用内容的类型的任何信息(类类型参数,方法类型参数,接口类型参数)。因此,即使您可能具有特定方法的重载,也不能将它们绑定到仅在其泛型类型参数上不同的多个接口实现。

总的来说,我可以看到为什么你认为这段代码有气味。但是,为了向您提供更好的解决方案,有必要了解您的需求。为什么要首先使用通用接口?