Java-интерфейс расширяет вопросы - PullRequest
3 голосов
/ 06 марта 2009

Я должен реализовать сервер 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?

Ответы [ 6 ]

15 голосов
/ 06 марта 2009

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

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

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. Не уверен, что это хороший подход.

5 голосов
/ 06 марта 2009

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

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

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

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

2 голосов
/ 06 марта 2009

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

С этим я бы предложил вам:

  • Напишите API, который вы хотите выставить
  • Напишите реализации, которые соединяют ваш API и внутренние сервисы
2 голосов
/ 06 марта 2009

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

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

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

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

1 голос
/ 06 марта 2009

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

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

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

1 голос
/ 06 марта 2009

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...