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

Сначала немного предыстории:

У меня уже есть класс Result, который я реализовал для передачи информации о результате. Они были реализованы специально для неудач, успешный результат обычно не несет никакой дополнительной информации. Я использую их в публичных методах на внешней стороне основного API в одном из моих проектов, или в методах на один или два уровня глубже, где мне нужно передать подробную информацию обратно в публичный API. Этот API используется в контейнере и клиенте командной строки, и ранее он был очень счастлив к исключениям. Альтернативой было добавление этой информации о контексте сбоя в исключения и добавление еще нескольких отдельных классов исключений, где им просто не место. Думаю, это отражает мой общий подход:

http://blogs.atlassian.com/2011/05/exceptions_are_bad

Result имеет поддерживающие перечисления и интерфейс:

  1. ResultStatus: SUCCESS, FAILURE, CONDITIONAL_SUCCESS и т.д. Хранит коды возврата int и общие сообщения (например, "Операция прошла успешно")
  2. ResultCode: Пустой интерфейс для обозначения кодов результатов перечисления, FILE_NOT_FOUND, FILE_INVALID, FILE_INCOMPATIBLE. Они в основном используются для модульного и функционального тестирования, но также используются для онлайн помощи (помощь Maven по сбоям функционально похожа на то, что я делаю)

Result предоставляет статические методы фабрики для статуса и методы конструктора для установки других ключевых полей и является неизменяемым. Вот как выглядит создание результата:

Result.success().withCode( ResultCode ).withMessage( "" );

И оценка возвращаемого результата:

result.isSuccessful();
result.hasCode( ResultCode );
result.getMessage();

Моя проблема:

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

Я недоволен своей первой попыткой: ResultPair, который содержит результат и значение, где T - тип значения. Если я не продублирую все методы Result, использование класса будет многословным и неуклюжим: получить Result, добавить его в ResultPair, выловить результат и значение перед оценкой. На первый взгляд, наследование не работает из-за паттерна builder, и я не могу придумать способ получить ясный, чистый код, который позволяет Result, без полного дублирования класса Result.

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

Я действительно не против иметь два класса, и это не конец света - дублировать код построения и оценки результата, просто это кажется неправильным, и я чувствую, что недостаток опыта берет надо мной верх, и я упускаю (очевидное или не очень очевидное) решение. Я использую Guava и хочу дружелюбно запретить нулевые значения, поэтому могу дополнительно обернуть все значения в Optional, но мне все равно понадобятся общие параметры (и компоновать их в класс, чтобы избежать result.value().get()...). Ладно, теперь я думаю вслух. Я должен задать вопрос...).

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

7
задан Danny Thomas 13 January 2012 в 23:39
поделиться