Что-то может пойти не так, как надо, но это не исключение

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

Например:

Я разрабатываю монополистическую игру. Класс Банк имеет метод buyHouse и поле, которое считает число домов оставленным (существует 32 здания в монополии). Что-то, что могло пойти не так, как надо, является плеером, покупающим дом, когда существует 0 оставленных. Как я должен обработать это. Вот 3 подхода, которые я могу придумать.

1. public void buyHouse(Player player, PropertyValue propertyValue)
{
    if(houseCount < 0) throw new someException;
    ....
    //Not really an exceptional situation
}

2. public boolean buyHouse(Player player, PropertyValue propertyValue)
{
    if(houseCount < 0) return false;
    ....
    //This I think is the most normal approach but changing something
    //and returning if it was a success seems bad practice to me.
}

3. public boolean housesLeft()
{
    if(houseCount > 0) return true;

    return false;

    //Introducing a new method. But now I expect the client to call this method
    //first before calling buyHouse(). 
}

Что Вы сделали бы?

10
задан matt b 15 July 2010 в 01:58
поделиться

10 ответов

Я бы сделал 3 и 1 вместе. Правильное использование API заключается в том, чтобы проверить, остались ли дома, прежде чем купить один из них. Если, однако, разработчик забыл это сделать, то выбросьте исключение времени выполнения.

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

12
ответ дан 3 December 2019 в 14:43
поделиться

Допускается вариант (1) или (2), в зависимости от того, считаете ли вы «отсутствие домов на покупку» обычным результатом или исключительным условием.

(3) - плохая идея по нескольким причинам:

  • он нарушает инкапсуляцию (клиент должен слишком много знать о внутреннем устройстве банка)
  • вам все равно придется проверять наличие ошибок и выполнять (1 ) или (2) в случае, если клиент ошибается
  • , это проблематично в многопоточных ситуациях
2
ответ дан 3 December 2019 в 14:43
поделиться

Пара других вариантов:

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

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

  • Ваш метод может возвращать объект HousePurchaseTransaction , который сам может быть запрошен для определения успеха или неудачи транзакции, ее фактической стоимости и т. Д.

  • Более богатый вариант этой темы может заключаться в том, чтобы сделать HousePurchaseTransaction абстрактным и получить два дочерних класса: SuccessfulHousePurchase и FailedHousePurchase , чтобы вы могли связать другое поведение с два условия результата.Для установки дома на улице вам может потребоваться передать объект SuccessfulHousePurchase. (это позволяет избежать опасности возврата нуля, который является корнем более поздней ошибки нулевой ссылки, и является вариантом шаблона нулевого объекта)

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

2
ответ дан 3 December 2019 в 14:43
поделиться

Я нахожу значение слова "исключительный" довольно субъективным. Оно означает все, что вы хотите, чтобы оно означало. Вы разрабатываете интерфейс к функции, вам и решать, что является исключительным, а что нет.

Если вы не ожидаете, что функция buyHouse будет вызываться, когда houseCount <= 0, то исключение здесь вполне уместно. Даже если вы ожидаете, что оно будет вызвано, вы можете перехватить исключение в вызывающей программе, чтобы справиться с этой ситуацией.

4
ответ дан 3 December 2019 в 14:43
поделиться

Я бы сделал что-то вроде этого:

public boolean BuyHouse(Player player, PropertyValue propertyValue) {
      // Get houseCount
      if(houseCount <= 0) {
       // Log this to your message queue that you want to show 
       // to the user (if it has a UI)
       return false;
      }
      // Do other stuff if houses are left
}

PS: Я не знаком с java, Я использую C #

1
ответ дан 3 December 2019 в 14:43
поделиться

Это очень похоже на идею извлечения элемента из пустого стека... это исключительный случай. Вы делаете что-то, что должно не получиться.

Думайте об исключительных ситуациях как о случаях, когда вы хотите уведомить программиста, что что-то пошло не так, и вы не хотите, чтобы он это проигнорировал. Использование простого булева возвращаемого значения не является "правильным", поскольку программист может просто проигнорировать его. Также идея иметь метод, который должен вызываться для проверки наличия свободных домов, является хорошей идеей. Но помните, что в некоторых случаях программисты забудут его вызвать. В этом случае исключение служит для напоминания о необходимости вызова метода для проверки наличия дома перед его приобретением.

Итак, я бы предоставил метод для проверки наличия домов и ожидал, что люди вызовут его и будут использовать возвращаемое значение true/false. В случае, если они не вызовут этот метод или проигнорируют возвращаемое значение, я бы выбросил исключение, чтобы игра не перешла в плохое состояние.

5
ответ дан 3 December 2019 в 14:43
поделиться

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

Учитывая ситуацию, которую вы описываете, я считаю, что использование исключений нецелесообразно, поскольку после продажи 32 домов банк по-прежнему не будет в них (это новое «нормальное» состояние), а обработка исключений на самом деле довольно хороша. медленнее в Java по сравнению с обычной обработкой.

Одна вещь, которую вы могли бы сделать, - это более точно отразить реальное взаимодействие. В «Монополии» банкир просто скажет вам, что вы не можете купить дом, если его не осталось.

Возможная модель для этого выглядит следующим образом:

public House buy(Player player, PropertyValue propertyValue) {
  House propertyHouse = null;
  if (houseCount > 0) {
    propertyHouse = new House(player, propertyValue);
    houseCount--;
  }

  return propertyHouse;
}

Это также позволит вам добавить поведение к объекту House и сделать процесс запроса / покупки дома немного более естественным. Если домов нет в наличии, вы их не получите.

3
ответ дан 3 December 2019 в 14:43
поделиться

На этот вопрос трудно ответить без контекста, в котором сущность имеет дом. С точки зрения общего дизайна, существует небольшая семантическая разница для вызывающего между (1) и (2) - оба являются попыткой и проверкой, - но вы правы, что (1) следует избегать из-за полностью ожидаемого состояния.

1
ответ дан 3 December 2019 в 14:43
поделиться

Здесь вы выбираете правило и исключение для пользователя, который использует ваш API / методы:

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

Это похоже на проверку перед доступом к определенному элементу массива: вы проверяете длину массива, прежде чем пытаться получить к нему доступ, иначе возникнет исключение.

Это должно выглядеть так:

if (housesLeft() > 0) buyHouse(...);

похоже на

for (int i=0; i < arrayList.length; i++) System.out.println(arrayList[i]);
1
ответ дан 3 December 2019 в 14:43
поделиться

Помните, что вы можете использовать

return houseCount > 0;

, а не

if(houseCount > 0) return true;

return false;
1
ответ дан 3 December 2019 в 14:43
поделиться
Другие вопросы по тегам:

Похожие вопросы: