Я пишу Java-реализацию карточной игры, поэтому я создал специальный тип Collection, который я называю Zone. Все методы модификации Java Collection не поддерживаются, но в Zone API есть метод move(Zone, Card)
, который перемещает карту из заданной зоны в себя (выполняется с помощью методов, закрытых для пакетов). Таким образом, я могу гарантировать, что никакие карты не будут удалены из зоны и просто исчезнут; они могут быть перемещены только в другую зону.
Мой вопрос: насколько необходим этот вид защитного кодирования? Это «правильно» и похоже на правильную практику, но это не значит, что Zone API когда-либо станет частью какой-то публичной библиотеки. Это только для меня, так что вроде как я защищаю свой код от себя, когда я, возможно, мог бы быть более эффективным, просто используя стандартные Коллекции.
Как далеко я должен взять эту идею Зоны? Может ли кто-нибудь дать мне несколько советов о том, как много я должен думать о сохранении контрактов в классах, которые я пишу, особенно для тех, которые на самом деле не будут общедоступными?
источник
Ответы:
Я не собираюсь решать проблему дизайна - просто вопрос о том, нужно ли делать вещи «правильно» в непубличном API.
В том-то и дело. Возможно, есть программисты, которые помнят нюансы каждого класса и метода, которые они когда-либо писали, и никогда не ошибочно обращаются к ним с неправильным контрактом. Я не один из них. Я часто забываю, как написанный код должен работать в течение нескольких часов после его написания. После того, как вы думаете, что сделали это правильно один раз, ваш ум будет стремиться переключиться на проблему, над которой вы работаете сейчас .
У вас есть инструменты для борьбы с этим. Эти инструменты включают (в произвольном порядке) соглашения, модульные тесты и другие автоматические тесты, проверку предварительных условий и документацию. Я сам посчитал, что модульные тесты неоценимы, потому что они заставляют вас задуматься о том, как будет использоваться ваш контракт, и предоставят документацию о том, как был разработан интерфейс.
источник
Я обычно следую некоторым простым правилам:
IllegalArgumentException
).assert input != null
).Если клиент действительно заинтересован в этом, он всегда найдет способ заставить ваш код плохо себя вести. Они всегда могут сделать это через отражение, по крайней мере. Но это красота дизайна по контракту . Вы не одобряете такое использование своего кода, и поэтому не можете гарантировать его работу в таких сценариях.
Что касается вашего конкретного случая, если
Zone
посторонние не должны использовать его и / или получать к нему доступ, либо сделайте класс package-private (и, возможно,final
), либо, предпочтительно, используйте коллекции, которые Java вам уже предоставляет. Они проверены, и вам не нужно изобретать велосипед. Обратите внимание, что это не мешает вам использовать утверждения во всем коде, чтобы убедиться, что все работает как положено.источник
Оборонительное программирование - это очень хорошая вещь.
Пока это не начинает мешать написанию кода. Тогда это не такая хорошая вещь.
Говоря немного более прагматично ...
Похоже, вы находитесь на грани того, чтобы зайти слишком далеко. Задача (и ответ на ваш вопрос) заключается в понимании бизнес-правил или требований программы.
Используя в качестве примера API-интерфейс вашей карточной игры, в некоторых ситуациях все, что можно сделать для предотвращения мошенничества, имеет решающее значение. Могут быть задействованы большие суммы реальных денег, поэтому имеет смысл поставить большое количество чеков на место, чтобы избежать мошенничества.
С другой стороны, вы должны помнить о принципах SOLID, особенно о единоличной ответственности. Просить контейнерный класс эффективно проверять, куда идут карты, может быть немного. Может быть лучше иметь уровень аудита / контроллера между контейнером карты и функцией, которая получает запросы на перемещение.
В связи с этими проблемами вы должны понимать, какие компоненты вашего API публично представлены (и, следовательно, уязвимы) по сравнению с частными и менее уязвимыми. Я не сторонник «твердого внешнего покрытия с мягкой внутренней частью», но лучший результат ваших усилий - укрепить внешний вид вашего API.
Я не думаю, что предполагаемый конечный пользователь библиотеки так же критичен в определении того, сколько защитного программирования вы внедрили. Даже с модулями, которые я пишу для собственного использования, я по-прежнему применяю меры проверки, чтобы убедиться, что в будущем я не совершил непреднамеренную ошибку при вызове библиотеки.
источник
Защитное кодирование - это не просто хорошая идея для открытого кода. Это отличная идея для любого кода, который не сразу выбрасывается. Конечно, вы знаете, как это должно называться сейчас , но вы не представляете, насколько хорошо вы будете помнить эти шесть месяцев спустя, когда вернетесь к проекту.
Базовый синтаксис Java дает вам много встроенной защиты по сравнению с языком нижнего уровня или интерпретируемым языком, таким как C или Javascript соответственно. Предполагая, что вы называете свои методы четко и не имеете внешнего «секвенирования методов», вы, вероятно, можете просто указать аргументы в качестве правильного типа данных и включить разумное поведение, если правильно типизированные данные все еще могут быть недопустимыми.
(С другой стороны, если карты всегда должны быть в зоне, я думаю, что вы получите лучший результат, если все карты в игре будут ссылаться на коллекцию, глобальную для вашего объекта Game, и иметь свойство Zone быть свойством каждую карту. Но поскольку я не знаю, чем занимаются ваши зоны, кроме как держать карты, трудно понять, подходит ли это.)
источник
CardDescriptor
, есть карта, которая содержит карту, ее местоположение, статус «вверх / вниз» или даже ротацию для игр, которые заботятся об этом. Это все изменяемые свойства, которые не изменяют личность карты.Сначала создайте класс, который хранит список зон, чтобы вы не потеряли зону или карты в ней. Затем вы можете проверить, что перевод находится в вашем ZoneList. Этот класс, вероятно, будет своего рода одноэлементным, так как вам понадобится только один экземпляр, но вы можете захотеть установить наборы зон позже, так что держите ваши параметры открытыми.
Во-вторых, не используйте Zone или ZoneList для реализации Collection или чего-либо еще, кроме случаев, когда это требуется. То есть, если Zone или ZoneList будут переданы чему-то, что ожидает коллекцию, то реализуйте это. Вы можете отключить несколько методов, заставив их вызвать исключение (UnimplementedException или что-то в этом роде) или просто заставив их ничего не делать. (Подумайте очень серьезно, прежде чем использовать второй вариант. Если вы сделаете это, потому что это легко, вы обнаружите, что пропускаете ошибки, которые могли бы быть обнаружены на раннем этапе.)
Есть реальные вопросы о том, что является «правильным». Но как только вы поймете, что это такое, вы захотите поступить таким образом. Через два года вы забудете обо всем этом, и если вы попытаетесь использовать код, то вы будете очень раздражены парнем, который написал его таким нелогичным образом и ничего не объяснил.
источник
Защитное кодирование в дизайне API обычно заключается в проверке правильности ввода и тщательного выбора правильного механизма обработки ошибок. Стоит также отметить и другие ответы.
Это на самом деле не то, что ваш пример. Вы ограничиваете свою поверхность API по очень конкретной причине. Как упоминает GlenH7 , когда набор карт должен использоваться в реальной игре, например, с колодой («используемой» и «неиспользованной»), столом и руками, вы определенно хотите поставить чеки, чтобы убедиться, что каждый Карта из набора присутствует один раз и только один раз.
То, что вы разработали это с «зонами», является произвольным выбором. В зависимости от реализации (в приведенном выше примере зона может быть только рукой, колодой или столом), она вполне может быть тщательной.
Однако эта реализация звучит как производный тип более
Collection<Card>
похожего набора карт с менее ограничительным API. Например, если вы хотите создать калькулятор стоимости руки или AI, вы, безусловно, хотите свободно выбирать, какую и сколько карт вы будете повторять.Так что хорошо бы выставить такой ограничительный API, если единственная цель этого API - убедиться, что каждая карта всегда находится в зоне.
источник