Общий интерфейс

Допустим, я хотел определить интерфейс, который представляет собой вызов удаленного сервиса. Теперь вызов удаленной службы обычно что-то возвращает, но может также включать входные параметры. Предположим, что реализующий класс обычно реализует только один метод службы. Учитывая приведенную выше информацию, следующий плохой дизайн (это не совсем правильно):

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 кажется действительно уродливым. Кроме того, параметр типа B в IExecutesService<A,B> будет избыточным для реализующего класса, который вызывает службу без каких-либо входных параметров. Также кажется излишним создание двух отдельных интерфейсов для двух разных сервисных вызовов.

Ответ 1

Здесь одно предложение:

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

Из-за стирания типа любой класс сможет реализовать только один из них. Это исключает, по крайней мере, избыточный метод.

Это не необоснованный интерфейс, который вы предлагаете, но я не уверен на 100% того, что он добавляет. Вы можете просто использовать стандартный Callable интерфейс. Он не поддерживает аргументы, но эта часть интерфейса имеет наименьшее значение (imho).

Ответ 2

Вот еще одно предложение:

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

Ответ 3

Я бы остался с двумя разными интерфейсами.

Вы сказали, что "я хочу сгруппировать моих исполнителей услуг в рамках общего интерфейса... Также кажется, что избыток создает два отдельных интерфейса для двух разных вызовов службы... Класс будет реализовывать только один из этих интерфейсов"

Не понятно, в чем причина того, что у вас есть один интерфейс. Если вы хотите использовать его как маркер, вы можете просто использовать аннотации вместо этого.

Другим моментом является то, что существует возможный случай, когда ваши требования меняются, и методы (методы) с другой сигнатурой появляются на интерфейсе. Конечно, возможно использовать шаблон адаптера, но было бы довольно странно видеть, что конкретный класс реализует интерфейс, скажем, тремя способами, в которых два из них удаляют UnsupportedOperationException. Возможно, появится четвертый метод и т.д.

Ответ 4

Как ответ строго в соответствии с вашим вопросом, я поддерживаю предложение cleitus.


Вы также можете использовать интерфейс маркера (без метода), скажем DistantCall, с несколькими несколькими суб-интерфейсами, которые имеют точные подписи, которые вы хотите.

  • Общий интерфейс будет отмечать все их, если вы хотите написать для них общий код.
  • Количество конкретных интерфейсов может быть уменьшено с помощью общей сигнатуры cleitus.

Примеры интерфейсов "многоразового использования":

    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

Я не думал ни о каком экземпляре в вызове. Я думал, что ваш код вызова знал, что он звонил, и вам просто нужно собрать несколько удаленных вызовов в общем интерфейсе для какого-то общего кода (например, для проверки всех удаленных вызовов по соображениям производительности). В вашем вопросе я не заметил, что код вызова является общим:-(

Если это так, я предлагаю вам иметь только один интерфейс, только одну подпись. Несколько из них только принесут больше сложности, ни для чего.

Однако вам нужно задать себе несколько более широких вопросов:
как вы обеспечите правильную связь вызывающего и вызываемого абонентов?

Это может быть следствием этого вопроса или другим вопросом...

Ответ 5

Если я правильно понял, вы хотите, чтобы один класс реализовал несколько таких интерфейсов с разными параметрами ввода/вывода? Это не будет работать на Java, потому что генерики реализуются через стирание.

Проблема с дженериками Java заключается в том, что дженерики на самом деле ничего, кроме магии компилятора. Во время выполнения классы не содержат никакой информации о типах, используемых для генерических материалов (параметры типа класса, параметры типа метода, параметры типа интерфейса). Поэтому, даже если у вас могут быть перегрузки определенных методов, вы не можете привязывать их к нескольким реализациям интерфейса, которые отличаются только их параметрами общего типа.

В общем, я понимаю, почему вы думаете, что этот код имеет запах. Однако, чтобы предоставить вам лучшее решение, вам нужно будет узнать немного больше о ваших требованиях. Почему вы в первую очередь используете общий интерфейс?