Интерфейс Java расширяет вопросы

Мне нужно реализовать сервер RMI, который будет интерфейсом для двух других служб RMI. Поэтому я решил, что будет логично, если интерфейс для этого реализует интерфейсы для двух других сервисов.

public interface FrontEndServer extends Remote, BookServer, StudentServer
{
    // Block empty so far
}

Однако на StudentServer есть метод

/**
 * Allows a student to borrow a book
 * 
 * @param studentID of the student who wishes to borrow a book
 * @param bookID of the book the student wishes to borrow
 * @throws RemoteException
 * @throws StudentNotFoundException when a student is not found in the system
 */
void addBookToStudent(int studentID, int bookID) throws RemoteException, StudentNotFoundException;

Я хотел бы, чтобы FrontEndServer также выдавал BookNotFoundException, поскольку этот сервис также проверяет, существует ли книга на самом деле, прежде чем пытаться добавить подробности.

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


person Malachi    schedule 05.03.2009    source источник


Ответы (6)


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

Подумай об этом:

interface A {
  void foo();
}

interface B extends A {
  void foo() throws IOException;
}

A a = new B() { ... }
a.foo();

потенциально может вызвать IOException, но вы не сможете узнать об этом. Вот почему вы не можете этого сделать.

Это, конечно, вполне приемлемо:

interface A {
  void foo() throws IOException;
}

interface B extends A {
  void foo();
}

A a = new B() { ... }
try {
    a.foo();
} catch (IOException e) {
    // must catch even though B.foo() won't throw one
}

Однако ваш BookNotFoundException может расширить RuntimeException или RemoteException. Однако не уверен, что это хороший подход.

person cletus    schedule 05.03.2009
comment
Спасибо. Похоже, я просто реализую методы во FrontEndServer, хотя, думаю, это имеет смысл, если честно. - person Malachi; 06.03.2009
comment
хотя, если вы хотите определить исключение «книга не найдено» как тип RuntimeException, вы можете выбросить его, не объявляя. Но это зло. - person Guss; 06.03.2009

Что хорошего в одном типе, который расширяет оба этих интерфейса? Вы что-нибудь теряете, когда клиент зависит от двух разных объектов?

Чрезмерное использование наследования — распространенная ошибка новичков, потому что «наследование» — одна из примечательных особенностей объектно-ориентированного программирования. Однако в большинстве случаев композиция является лучшим выбором. В таком случае, почему бы не иметь два отдельных сервиса? Затем добавление CafeteriaService и DormitoryService позже не повлияет на существующие интерфейсы.

Что касается дизайна, метод addBookToStudent выиграл бы от возможности генерировать BookNotFoundException. Интерфейсы хрупки в том смысле, что любое их изменение приводит к поломке большого количества кода. Вы должны быть очень осторожны в их первоначальном дизайне. Например, BookNotFoundException, вероятно, слишком специфично; не может ли существовать множество исключений, препятствующих «добавлению» книги ученику? (Я предполагаю, что студенты берут книги в библиотеке во временное пользование.) Например: CheckOutLimitExceededException, UnpaidFinePendingException, AdultLiteraturePermissionException и т. д.

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

person erickson    schedule 05.03.2009

Несколько идей для вас:

  1. Объявите метод addBookToStudent в интерфейсе, чтобы создать исключение BookNotFoundException. Несмотря на то, что StudentServer может никогда не генерировать исключение, это не означает, что вы все равно не можете поместить его в интерфейс.

  2. Вы можете создать новое исключение — ObjectNotFoundException и наследовать от него BookNotFoundException и StudentNotFoundException, а затем объявить addBookToStudent для создания исключения ObjectNotFoundException.

  3. Что я, вероятно, сделал бы в «реальной жизни» — пусть StudentServer общается с BookServer, чтобы проверить идентификатор книги и выдать само исключение, а не выполнять эту проверку на FrontEndServer. Особенно, если StudentServer на самом деле будет напрямую использоваться чем-то другим, кроме FrontEndServer.

person Eric Petroelje    schedule 05.03.2009

Я бы посоветовал вам попытаться отделить открытый API от API, используемого для реализации вашей функциональности. Я предполагаю, что цель внешнего интерфейса служб RMI состоит в том, чтобы обеспечить разделение и стабильность от вызывающего приложения.

При этом я бы посоветовал вам:

  • Напишите API, который вы хотите предоставить
  • Напишите реализации, которые связывают ваш API и серверные службы.
person Kam    schedule 05.03.2009

Теоретически это возможно, если BookNotFoundException расширяет RemoteExcepiton.

Однако я предполагаю, что у вас нет контроля над интерфейсом StudentServer. Похоже, цель этого интерфейса — не вызывать исключение BookNotFoundException. Хотя я могу понять, почему вы этого хотите, интерфейс, похоже, этого не поощряет.

person Rob Di Marco    schedule 05.03.2009

Исключения вызываются методами, а не интерфейсами или классами. Поэтому, если в интерфейсе BookServer есть метод, он может вызвать исключение, когда вы добавите его в интерфейс.

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

Если вы задумаетесь об этом, то увидите, что это логично. Ваш FrontEndServer можно использовать как BookServer с помощью кода. Код во время компиляции ожидает исключения, определенные в BookServer. Затем во время выполнения BookServer внезапно создает исключение, которое не определено в интерфейсе BookServer. Если этот фрагмент кода знает только, что BookException является неожиданным, нет операторов catch или throws для его обработки.

person DefLog    schedule 05.03.2009