Статьи

Несколько уроков, которые я узнал о разработке API

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

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

Урок № 1. Не жертвуйте гибкостью и расширяемостью, защищая своих пользователей

Все началось с этой темы , в которой Щепан Фабер, создатель Mockito , упоминает, что недавно нашел модуль FEST Assertions, и это «кажется очень интересным». Он предположил, что для того, чтобы сделать библиотеку более расширяемой, класс Assertions и классы, реализующие методы assertion, можно сделать не финальными .

Я признаю, что был непреклонен, чтобы открыть эти классы. Я отклонил предыдущие запросы по следующим причинам:

  1. Класс Assertions содержит только статические методы: несколько перегруженных методов assertThat. Я подумал, что это «лучшая практика» для доступа к статическим методам только из объявленного класса (у компилятора Eclipse даже есть параметр для принудительного применения этого). Я, честно говоря, не видел смысла в подклассах утверждений. Я подумал, что если кому-то понадобится расширить его, он или она может использовать композицию вместо этого.
  2. Классы, содержащие фактические методы утверждений, также являются окончательными (например, StringAssert ) из-за отсутствия собственных типов в языке Java. Я объясню на примере. Методы утверждения в StringAssert всегда возвращают это, чтобы облегчить цепочку методов
    public StringAssert isNotNull() {
    assertNotNull();
    return this;
    }

    так что мы можем написать что-то вроде

    assertThat(someString).isNotNull()
    .isEqualTo("Hello World");

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

    @Override public MyStringAssert isNotNull() {
    super.isNotNull();
    return this;
    }

     

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

Щепан ответил:

Хм, я вижу смысл сейчас. Я все еще рекомендовал бы не финализировать классы assert … Внутренне, вы, вероятно, можете найти какой-то способ проверки, если вы не забыли переопределить (например, обширный набор функциональных тестов, правило PMD или тест JUnit, который отражает методы и т. Д.). Внешне я хотел бы иметь простой способ расширить утверждения для уже обработанных типов. Расширение через пользовательские условия не работает для меня, потому что это слишком Hamcrest, и я делаю FEST здесь, верно? ?

Szczepan предоставляет пример использования API FEST, о котором я никогда раньше не думал. Мало того, он также предоставляет прагматичное решение «проблемы», которой я боялся!

Вскоре после прочтения его ответа я понял, что слишком переживаю из-за того, что пользователи не следуют определенной «хорошей практике» или забывают вернуть правильный тип. Наихудшие сценарии не так плохи, как я думал, и пользователи API FEST могут легко оправиться от ошибки. Я понял, что ограничиваю расширяемость и гибкость API чрезмерной защитой пользователей.

Урок № 2. Слишком много вариантов может привести к путанице

В том же потоке Щепан предлагает добавить методы, которые есть и имеет как «гуманизированные» псевдонимы для удовлетворения, чтобы сделать API более компактным и читаемым. Следующий пример взят из вики FEST:

assertThat("hello").as("Greeting").satisfies(isUppercase());
// using the "is" alias:
assertThat("hello").as("Greeting").is(uppercase());

Хотя это имеет большой смысл (и я фактически подал задачи, чтобы иметь этот псевдоним в нашем следующем выпуске), Ансгар Конерманн, другой пользователь FEST, поднимает очень интересный момент:

Мне нравятся утверждения FEST для его простого API. Я считаю, что это значительный плюс для привлечения новых пользователей. Мы должны позаботиться о том, чтобы не усложнять API без необходимости. Когда я был новичком в FEST API, я даже считал странным иметь как (), так и описанный (), которые делают одно и то же. Позже я понял, что это техническая необходимость разрешить использование FEST в Groovy. Я бы хотел, чтобы FEST максимально упрощал API.

Подумайте о http://c2.com/xp/OnceAndOnlyOnce.html

Я бы согласился, что удовлетворяет / не отвечает требованиям звучит как математика, а не как проверка условий домена. Тем не менее, я чувствую, что эта базовая концепция условия / предиката абсолютно допустима для использования при определении тестов. Мы также согласны с тем, что assertThat подходит для всех тестов, однако ваш бизнес-аналитик, вероятно, выразит это немного иначе (например, makeSureThat, itMustAlwaysBeTheCase). Если бы кто-то спросил меня, я бы предпочел не включать каждый вариант, который естественный язык мог развиться для одного и того же значения с течением времени, а передать это значение, используя ровно одно, тщательно подобранное слово.

Ансгар имеет действительную точку зрения. Псевдонимы могут добавить больше гибкости API, но это также может увеличить путаницу для его пользователей. Наконец он добавляет:

В нашей команде ожидается, что API FEST: введите assertThat (фактический), нажмите CTRL + Пробел и мгновенно _know_, какой единственный метод вызывать для проверки, которую вы хотите выполнить. Больше не нужно думать о том, какой метод выбрать. Учитывая тестовое намерение, должно быть совершенно очевидно, какой метод вызывать. Псевдоним делает это сложнее. Не заставляйте меня думать — по крайней мере, не о методах, которые мне нужно вызывать. При написании тестов я вместо этого хочу сосредоточить свое внимание на условиях, которые следует проверять.

Довольно четкое сообщение. Больше нечего добавить.

В заключении

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

Как я упоминал ранее, работа с открытым исходным кодом — это отличный опыт. Это помогает нам оставаться скромными, потому что мы всегда найдем кого-то с идеями лучше, чем у нас.

Спасибо Щепан и Ансгар!

С http://alexruiz.developerblogs.com/