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

18

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

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

Что мне интересно, так это то, что в этой библиотеке я должен был создать getAccount(accountName)учетную запись, которая получит учетную запись, если она существует, и если она не создаст ее и не вернет информацию, это плохая вещь? Должен ли я оставить это клиенту для обработки исключений или просто назвать его что-то вроде getOrCreateAccount? Это имеет значение?

Плохо ли создавать что-то в операции get?

Майк
источник
9
По крайней мере, я бы назвал это getOrCreateAccountили подобное.
Теластин
4
Это кажется действительным в контексте ленивой инициализации. Однако это зависит от того, нужно ли вам использовать этот шаблон в вашей библиотеке.
Крис С
1
Мне нравится глагол acquire, нравится acquireAccount. Он не имеет существующего значения ни в каких основных протоколах, с которыми я сталкивался, и имеет императивное звено, которое ему подходит. «Делай все, что тебе нужно, чтобы приобрести один из них для меня. Проси, собирай, фальсифицируй, кради, мне все равно, просто дай мне один или умри пытаясь».
Дэн Росс
2
«Клиент всегда захочет создать учетную запись» - это кажется необычным. Если я не могу получить учетную запись, потому что пользователь неправильно набрал свое имя пользователя, я, конечно, не хочу создавать учетную запись с ошибочным именем.
gnasher729
Согласно спецификации javabean oracle.com/technetwork/articles/javaee/spec-136004.html getSomething() предназначен для геттеров и setSomething()для сеттеров. Имо все , что делает что - то более интеллектуальное должно называться что - то другое, то есть fetchSomething, obtainSomething, computeSomethingили и doSomethingElseт.д.
ccpizza

Ответы:

31

Да, это важно На мой взгляд, это вообще плохая практика - создавать что-то в процедуре, которая не документирована как обладающая способностью создания. Либо назовите эту процедуру, getOrCreate...либо используйте отдельную create...процедуру, а затем, если вы действительно хотите, getOrCreate...сначала сделайте попытку get..., а в случае неудачи вызовите, create...а затем вызовите get....

Пользователь библиотеки, вероятно, не ожидает, что get...процедура создастся, если операция get завершится неудачно. Если они вдруг обнаружат, что их тестовые вызовы get...создали целую тонну данных, они, вероятно, будут весьма удивлены. И как они убирают это? Что если они напишут код, думая, что в случае ошибки получат ошибку, get...и они захотят справиться с этим по- своему ?

FrustratedWithFormsDesigner
источник
Спасибо, create на самом деле возвращает идентификатор, который возвращает get, поэтому мне не нужно было бы делать get... create... get...только первые два. Я поговорю с клиентом о том, потребуется ли ему когда-либо возможность просто вызывать a, getне желая создавать
Майк
6
@Mike: я все еще думаю, что это должно быть createв названии, просто чтобы быть на 100% ясным о том, что происходит.
FrustratedWithFormsDesigner
Да, я согласен, я планировал сделать что-то похожее на getOrCreate, потому что моя первоначальная мысль была просто получить get, но я понял, что не сразу понятно, что он также создает что-то на заднем плане
Майк
1
Это очень поздно, но getOrCreateимеет приоритет в популярной веб- среде
tex
18

Нет, это не «плохая практика». Пока вы и другие разработчики согласны с тем, что вы хотите, чтобы это работало, это просто прекрасно. В конце концов, это будет возвращение аккаунта, чего вы и хотите. То, что учетная запись создается «под капотом», не имеет значения для звонящего.

GrandmasterB
источник
10
Единственное исключение - если это общедоступный API. Более широкое сообщество уже согласилось с тем, что GET является бессильным и нулевым; другими словами, каждый раз выполняется одно и то же действие, и это безопасный метод, который не меняет состояние сервера. Это в REST Wiki . Кроме того, если API существует только в вашем маленьком мире, делайте все, что принято вашей группой.
jmort253
9
Я не согласен, даже для публичного API это хорошо - пока это имеет смысл в контексте того, что API должен делать. Нет смысла создавать несколько записей в API, когда одна из них сделает свое дело.
GrandmasterB
Для справки, это полностью внутренняя библиотека, и я могу рассказать команде, используя ее, как она работает
Майк
1
@ jmort253: Служба является нулевой, если у нее нет видимых для клиента побочных эффектов . Сервер может делать как ему угодно.
Кевин Клайн
1
@kevincline, я думаю, я думал об этом, скажем, с моей кредитной карты. Если сервер заряжает мою карту на основе запроса GET, но все равно выдает мне результат «отклонено» при каждом запуске, создается впечатление, что это противоречит духу GET. С учетом сказанного я понимаю вашу точку зрения. Сервер может рассчитывать количество запросов GET и блокировать меня после 3 запросов, тем самым ограничивая скорость, но без изменения каких-либо подробностей моей учетной записи. Я думаю, что это важное различие, когда говорят, что состояние сервера не изменено. Возможно, я должен сказать «состояние клиента на сервере» вместо этого
jmort253
10

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

Кевин Клайн
источник
+1 Как правило, GetOrCreateэто неправильная семантика, но получить объект, который может «логически» существовать, независимо от того, существует ли он физически, это нормально. Например, разреженный массив изменяемых элементов может не иметь никакого хранилища, выделенного для элемента 1 841 533, но все же позволяет этому элементу «извлекаться» путем создания нового объекта, его сохранения и возврата ссылки.
суперкат
4

Наиболее целесообразно создать 3 метода:

getAccount -> Который просто получает учетную запись.

createAccount -> Создает учетную запись.

getAccountAndCreateIfNeeded -> Выберите собственное имя;)

Почему разделение: у вас есть простой метод получения и создания. Это четкий метод тестирования для обоих. Для getAccount это не исключение, чтобы не найти аккаунт. Так что просто верните false или что-то в этом роде, это ожидается.

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

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

Люк Франкен
источник
Кроме того, из Чистого кода: «Если ваш метод не может сделать то, что подразумевает его имя, выведите исключение». У меня был бы блок Try / Catch внутри GetOrCreateIfneeded, который генерирует сбойный GET, а затем внутри метода Throw создается createAccount для любого типа исключения, возвращаемого для сбойного получения.
Грэм
Я мог бы предложить четвертый метод:, getAccountIfExistsкоторый либо получит учетную запись, либо покажет, что ее не существует без создания новой. Сам getAccountметод должен предполагать, что учетная запись существует, и, если нет, генерировать исключение.
суперкат
2

Это зависит от обстоятельств.

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

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

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

GordonM
источник