Минимальный API против Удобства

Я пытаюсь разработать интерфейс, который будет использоваться внутренне для моего приложения. Следуя примеру Google, я стремлюсь уменьшить общедоступную помеху API. Однако существуют некоторые удобные методы, которые определяются с точки зрения минимальных методов. Какие факторы я должен рассмотреть, поскольку я ищу баланс между удобством и опрятностью?

Пример Google: в HashBiMap (документ):

Почему BiMap не имеет никакого getKeyForValue () методом?

Мы действительно думали об этом (Doug Lea даже в полушутку предложил назвать это teg ()!). Но Вам действительно не нужен он; просто назовите инверсию () .get ().

FAQ наборов Google

Пример этого на Set интерфейс: add() и remove() минимальные методы, тогда как addAll() и removeAll() для удобства. addAll() мог быть реализован с точки зрения add(), таким образом, это действительно не дает клиенту новые возможности работы с a Set. Но это действительно очищает клиентский код.

Я рассмотрел создание a Utility класс, который включал бы больше удобных методов. Но тогда я убегаю от ООП, и я должен включать объект, управляемый в как аргумент в каждом вызове. Хотя я предполагаю, что это следует примеру Java Collections класс.

9
задан Nick Heiner 1 January 2010 в 05:05
поделиться

9 ответов

Я бы определенно поставлял дополнительные API всякий раз, когда есть шанс, что класс сможет (даже если сегодня он этого не делает) реализовать этот API более эффективно, чем клиент. (Например, Set.removeAll().). И вообще я бы поставлял дополнительные API всякий раз, когда он очищает код клиента.

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

.
4
ответ дан 4 December 2019 в 20:24
поделиться

Предложение большего количества методов делает переопределение виртуальных методов более сложным/опасным.

Рассмотрим, например, add() и addAll(). Вызовет ли addAll() функцию add()? Он может (это может быть простая обёртка, которая по очереди вызывает add() для каждого элемента), но это не обязательно. Так что если вы затем подкласс, и добавляете какой-нибудь новый инвариант (возможно, например, вы заставляете add() добавлять вещи в отдельный контейнер для хранения порядка вставки, или что-то в этом роде, есть много вариаций на контейнерах, которые полезны в разных приложениях), то теперь вы должны знать, вызывает ли addAll() функцию add(). Если да, то отлично, что ваш подкласс поддерживает правильное поведение. Но это не обязательно!

Конечно, вы можете решить все это с помощью соответствующей документации. Но это делает опасные вещи проще.

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

Иногда возникает ситуация, когда превращение утилиты в метод (а не в не-членский нефренд) дает некоторое превосходство в реализации. Примером этого является сортировка; обычно сортировка (массивов, деков, векторов и т.д.) должна быть не-членской, но для линкованных списков есть особое преимущество в том, чтобы сделать метод sort(). В частности, метод может манипулировать узловыми ссылками и, таким образом, использовать внутреннее слияние - что-то сложное или невозможное для любого разумного интерфейса связанного списка. В этих исключительных случаях я бы предложил сделать утилитные методы неперестраиваемыми и явно указать, какие методы они вызывают (и где это имеет смысл, в каком порядке). Это максимизирует шанс того, что подклассы не будут ломать вещи.

.
4
ответ дан 4 December 2019 в 20:24
поделиться

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

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

.
1
ответ дан 4 December 2019 в 20:24
поделиться

Пока утилитные методы действительно полезны, а не просто плод вашего воображения ("может когда-нибудь пригодиться, если оборотни покорят США"), я бы добавил их в исходные классы.

Следуя вашему примеру, addAll() и removeAll() - это утилитные методы, которые разумны и должны быть добавлены в Set. Но addEven() и removeEven() не являются разумными, даже если они могут быть полезны в каком-то конкретном случае.

Теперь, как определить, какие методы разумны? Только два способа: опыт и опыт.

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

.
0
ответ дан 4 December 2019 в 20:24
поделиться

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

interface List ...

и

class AbstractList implements List ...

в пакете java.util. Таким образом, клиент может подклассифицироваться из абстрактного класса и просто реализовать абстрактный метод.

Лично мне не стыдно было бы помещать удобные методы в класс утилиты. Нельзя программировать чистое OO на сломанном языке. Что здесь пропускает Java - это либо traits, либо методы расширений. Насколько я знаю, методы расширений обсуждаются для Java 7.

.
1
ответ дан 4 December 2019 в 20:24
поделиться

Не глядя на источник, я бы догадался, что ArrayList.addAll сначала гарантирует, что емкость достаточно велика, чтобы массив не изменял размер во время выполнения операции. Для класса утилиты это было бы невозможно, так как это внутренняя реализация.

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

.
0
ответ дан 4 December 2019 в 20:24
поделиться

Другая контрольная точка: Java's List имеет как add(), так и addAll().

Я думаю, что важно быть ясным в вашем собственном воображении, что является фундаментальным и удобным. Также известные как атомные (не могут быть далее разделены) и составные (могут быть образованы комбинацией атомных методов), соответственно.

Знание фундаментальных методов очень полезно для доказательства вещей о вашем коде; и для обеспечения того, что ваши пользователи действительно могут делать что угодно с вашим кодом, так что это полная (e. g. убедиться, что в удобных методах нет возможности, доступной только ).

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

Другими словами, они не помогают: 100%-ный фокус на том, чтобы сделать ее полезной, и пусть оттуда протекает удобство и полнота.

0
ответ дан 4 December 2019 в 20:24
поделиться

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

0
ответ дан 4 December 2019 в 20:24
поделиться

Я дополню ответ Джона Ф.:

Мне нужны все методы удобства, которые я считаю полезными, без всех остальных, которые мне не нужны. Firefox позволяет использовать такие вещи с помощью плагинов. Браузер поддерживает то, что должен поддерживать базовый браузер; однако, в соответствии с моими личными предпочтениями, я могу доработать его с помощью плагинов. Я рассматриваю методы удобства в том же свете.

Позвольте мне добавлять любые модули, которые мне нравятся, чтобы у меня были только те методы удобства, которые я хочу.

Есть много сторон:

http://martinfowler.com/bliki/HumaneInterface.html

1
ответ дан 4 December 2019 в 20:24
поделиться
Другие вопросы по тегам:

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