Как я могу избежать использования исключений для управления потоком?

StackOverflow https://stackoverflow.com/questions/345626

Вопрос

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

public CustomObject get(String key, Date ifModifiedSince)

В принципе, предполагается, что метод возвращает CustomObject связанный с key тогда и только тогда, когда объект был изменен после ifModifiedSince.Если система хранения не содержит key затем метод должен возвращать значение null.

Моя проблема заключается в следующем:

Как мне справиться со сценарием, в котором ключ существует, но объект имеет не были изменены?

Это важно, потому что некоторые приложения, использующие этот класс, будут веб-сервисами и веб-приложениями.Этим приложениям нужно будет знать, возвращать ли значение 404 (не найдено), 304 (не изменено) или 200 (хорошо, вот данные).

Решения, которые я взвешиваю, таковы:

  1. Генерирует пользовательское исключение, когда система хранения не содержит key
  2. Генерирует пользовательское исключение, когда ifModifiedSince терпит неудачу.
  3. Добавьте свойство status к CustomObject.Требовать, чтобы вызывающий проверял свойство.

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

Тем не менее, я склоняюсь к варианту 3.

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


Ответы на этот Вопрос, Перефразированные:

  1. Обеспечить contains метод и требовать, чтобы вызывающий вызывал его перед вызовом get(key, ifModifiedSince), выбросить исключение, если ключ не существует, вернуть значение null, если объект не был изменен.
  2. Оберните ответ и данные (если таковые имеются) в составной объект.
  3. Используйте предопределенную константу для обозначения некоторого состояния (UNMODIFIED, KEY_DOES_NOT_EXIST).
  4. Вызывающий реализует интерфейс, который будет использоваться в качестве обратных вызовов.
  5. Дизайн - отстой.

Почему я не могу выбрать Ответ № 1

Я согласен, что это идеальное решение, но это было то, от которого я уже (неохотно) отказался.Проблема такого подхода заключается в том, что в большинстве случаев, в которых будут использоваться эти классы, серверной системой хранения данных будет сторонняя удаленная система, такая как Amazon S3.Это означает, что a contains метод потребовал бы обратного перехода к системе хранения, за которым в большинстве случаев последовал бы еще один обратный переход.Потому что это это стоило бы как времени, так и денег, это не вариант.

Если бы не это ограничение, это был бы лучший подход.

(Я понимаю, что не упомянул этот важный элемент в вопросе, но я старался быть кратким.Очевидно, это было актуально.)


Заключение:

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

Это было полезно?

Решение

Похоже, вы действительно хотите вернуть два предмета:код ответа и найденный объект.Вы могли бы рассмотреть возможность создания облегченной оболочки, которая содержит оба варианта и возвращает их вместе.

public class Pair<K,V>{
  public K first;
  public V second;
}

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

Кроме того, если срок действия данных не истек, вы все равно можете вернуть их, но дайте им код 303, чтобы сообщить им, что он не изменился.серия 4xx будет работать в паре с null.

Другие советы

С данным требованием вы не можете этого сделать.

Если вы разработали контракт, затем добавьте условие и заставьте вызывающий объект вызывать

exists(key): bool

Реализация сервиса выглядит следующим образом:

if (exists(key)) {
    CustomObject o = get(key, ifModifiedSince);
    if (o == null) { 
      setResponseCode(302);
    } else {
      setResponseCode(200);
      push(o);
   }

} else {
      setResponseCode(400);
}

Клиент остается неизменным и никогда не заметит, что вы прошли предварительную проверку.

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

Затем вы должны придерживаться спецификаций и действовать следующим образом:

 CustomObject o = get(key, ifModifiedSince);

 if (o != null) {
     setResponseCode(200);
     push(o);
  } else {
     setResponseCode(404); // either not found or not modified.
  }

Хорошо, в данном случае вы не отправляете 302, но, вероятно, так оно и было задумано.

Я имею в виду, что по соображениям безопасности сервер не должен возвращать больше информации, чем это [ запрос get( ключ, дата ) возвращает только либо null, либо object ]

Так что не беспокойся об этом.Поговорите со своим менеджером и сообщите ему об этом решении.Прокомментируйте код и этим решением тоже.И если у вас есть архитектор в руках, подтвердите обоснование этого странного ограничения.

Скорее всего, вы этого не предвидели, и они могут изменить контракт по вашему предложению.

Иногда, желая действовать правильно, мы можем действовать неправильно и поставить под угрозу безопасность нашего приложения.

Общайтесь со своей командой.

Вы можете создать специальный конечный пользовательский объект в качестве "маркера" для обозначения неизмененного:

static public final CustomObject UNCHANGED=new CustomObject();

и проверьте соответствие с помощью "==" вместо .equals() .

Также может сработать возврат null при неизмененном и выдача исключения при не существует?Если бы мне пришлось выбрать один из ваших 3-х, я бы выбрал 1, потому что это кажется самым исключительным случаем.

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

public bool exists( String key ) { ... }

Вызывающий мог бы сделать:

if (exists(key)) {
   CustomObject modified = get(key,DateTime.Today.AddDays(-1));
   if (modified != null) { ... }
}

or

try {
    CustomObject modified = get(key,DateTime.Today.AddDays(-1));
}
catch (NotFoundException) { ... }

Проблема с исключениями заключается в том, что они предназначены для того, чтобы сигнализировать о сценарии "быстрого сбоя" (т.е.если не обработано, исключение будет остановка заявление) в связи с исключительным и ненормальный поведение.

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

Следовательно, я бы не использовал exception, а скорее задокументировал бы действие, которое необходимо выполнить вызывающему, чтобы правильно интерпретировать результат (свойство или специальный объект).

Насколько строгие требования предъявляются к сигнатуре этого метода?

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

Я бы обсудил это с вашим руководителем, если это возможно.

Я бы все равно вернул null.

Назначение свойства состоит в том, чтобы вернуть объект, который был изменен после указанной даты.Если возврат null ни для одного объекта не является нормальным, то, безусловно, возврат null для неизмененного объекта тоже является нормальным.

Лично я бы вернул null для неизмененного объекта и выдал исключение для несуществующего объекта.Это кажется более естественным.

Кстати, вы совершенно правы, что не используете исключения для управления потоком, поэтому, если у вас есть только эти 3 варианта, ваша интуиция верна.

Вы могли бы следовать шаблону .Net library of и иметь общедоступное статическое поле только для чтения в пользовательском объекте, называемом Пользовательский объект.Пустой это относится к типу Пользовательский объект (например, string.Пустой и Guid.Пустой).Вы могли бы вернуть это, если объект не изменен (потребителю функции нужно будет сравнить с ним).
Редактировать: Я только сейчас заметил, что вы работаете на Java, но принцип все еще применим

Это дает вам возможность выполнить следующие действия

  • Верните null, если ключ не существует.

  • Возврат Пользовательский объект.Пустой если ключ существует, но объект не был изменен.

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

Возможно, это свойство было бы более уместно назвать Пользовательский объект.Не изменен поскольку Empty действительно предназначен для типов значений, поскольку они не могут быть null.Также Не измененный это позволило бы легче донести значение поля до потребителя.

(Предполагаемый) интерфейс в отношении требований серьезно нарушен.Вы пытаетесь делать несвязанные вещи в рамках одного метода.Это дорога в программный ад.

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

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

public interface Callback {
  public void keyDoesNotExist();
  public void notModified(CustomObject c);
  public void isNewlyModified(CustomObject c);
  .
  .
  .
}

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top