20-й эпизод работы над Quiz down, приложением на базе Java + Spring, которое использует синтаксис, подобный Markdown, для создания тестов по кодированию.
Истории, Реализованные
После нетривиального рефакторинга (см. Никаких голых структур данных ниже), Теперь у меня есть возможность переходить от вопроса к вопросу. Генерация HTML теперь генерирует checked если этот выбор был выбран ранее. Следующий шаг – загрузить ответы пользователя из базы данных и отправить их контроллеру пользовательского интерфейса.
HTML: Используйте отключенный атрибут для кнопки или ссылки
Когда я искал подходящий класс CSS для отображения кнопки как отключенной, я обнаружил, что вместо использования, скажем, класса is-disabled CSS, HTML поддерживает disabled в качестве атрибута для обеих button и ссылка ( ). Например, чтобы показать ссылку как отключенную вместо:
вы бы использовали атрибут disabled следующим образом:
Никаких Голых Структур Данных
Одна из моих эвристик Java-дизайна – это Никаких голых структур данных , что означает обертывание необработанных структур данных ( Список , Карта и т.д.) в типе, чтобы вы могли передавать его по кругу и взаимодействовать с ним таким образом, который имеет смысл с точки зрения домена.
Это похоже на Нет строкового кода эвристика: не используйте String везде, вместо этого используйте типы классов и интерфейсов. Подробности см. здесь и здесь .
При добавлении функции, позволяющей пользователям переходить от вопросов к вопросам, мне нужно было, чтобы выбор, сделанный пользователем, был доступен. Ранее я сохранял эти варианты в виде Set , так как это было просто и не использовалось во многих местах. Однако, как только я нуждался в нем в большем количестве мест (перенося его обратно во внешний интерфейс из базы данных), передача “голого” Set сделала код немного сложнее для чтения и работы. Например, с помощью Set выяснение правильности ответа зависит от типа вопроса ( FIB – это заполнение пробела, а MC – множественный выбор):
public boolean isCorrectFor(@NonNull Setresponse) { return switch (questionType) { case FIB -> correctChoices.containsAll(response); case MC -> correctChoices.equals(response); }; }
Это довольно типичный код, но если вы не знакомы со спецификой Набор , возможно, вам придется немного прочитать JavaDoc, чтобы убедиться, что вы правильно его поняли. Заменив его типом Ответа , который инкапсулирует набор , приведенный выше код становится:
public boolean isCorrectFor(Response response) {
return switch (questionType) {
case FIB -> response.matchesAny(correctResponse);
case MC -> response.allMatch(correctResponse);
};
}
Теперь это может быть не таким уж удивительным улучшением (я все еще играю с именованием), но это приводит к следующему шагу (который я еще не сделал) по переносу поведения, специфичного для типа вопроса, в подкласс ответа, например, a Реакция Фибрилляции предсердий и Ответ , который будет выглядеть следующим образом:
public boolean isCorrectFor(Response response) {
return response.correctlyMatches(correctResponse);
}
Полностью делегирование того, как сравниваются ответы, классу, который лучше всего знает, как проводить это сравнение.
В моем обучении и обучении разработчиков Java использование строгих типов и открытых структур данных является причиной большой боли, когда что-то меняется, поэтому всегда старайтесь инкапсулировать строки в объекты значений и заключать ваши структуры данных в свой собственный класс.
Раскрытие актива() в ответе: нарушает инкапсуляцию?
Инкапсуляция Set внутри Response означает, что когда мне нужно отобразить варианты ответа или сохранить их в базе данных (т.Е. Передать ответ через границу домена ) Мне нужно получить эти варианты в виде набора, поэтому в классе Response есть метод
public SetasSet() { return Set.copyOf(response); // return a copy of our internal field }
Это возвращает выбор в виде Set . Я использую это в классе Graded Answer View , чтобы преобразовать его в разделенную запятыми Строку :
private static String responseAsString(Answer answer) {
return answer.response().asSet()
.stream()
.sorted()
.collect(Collectors.joining(", "));
}
Вы могли бы подумать: о нет, это нарушает инкапсуляцию, предоставляя доступ к set , но на самом деле я этого не делаю, потому что я возвращаю копию своего внутреннего хранилища вариантов ответов. Я никогда не раскрываю само поле, поэтому единственный способ изменить внутренние данные Response – это использовать только его методы. Изменение копии не будет иметь никакого эффекта.
Пустые varargs
Я не думаю, что осознавал это раньше, но вызов метода, определенного с помощью varargs (например, create From(String... строки) ) означает, что вы можете вызвать этот метод с без аргументов: create From() , и внутри этого метода аргумент strings
Это означает, что мне не нужно было в специальном случае превращать varargs в пустой набор, потому что вызов Set.of() без аргументов превратится в пустой набор (технически ImmutableCollections.emptySet() начиная с Java 9), что я бы хочу.
Совместные тесты
Большинство моих тестов являются модульными тестами, поскольку они тестируют код в классе, работая с этим классом как можно ближе. Например, Choice class может быть непосредственно протестирован без каких-либо других объектов. Однако Question Transformer не может быть протестирован сам по себе, поскольку его задача в основном заключается в сотрудничестве с более конкретными трансформаторами для выполнения своей работы, т.Е. Его задача – интегрировать или сотрудничать с другими объектами. Это означает, что мне не нужно всесторонне тестировать все поведение всех задействованных классов, просто один или два теста, которые гарантировали бы, что Вопросительный трансформатор правильно//сотрудничает с (в данном случае, распространяя ответ на) другие объекты.
Вы можете увидеть клип из моего стрима, где я делаю это: https://www.twitch.tv/videos/443315061 .
Будьте уверены и настройтесь на мой следующий прямой эфир по кодированию: https://twitch.tv/jitterted .
Хочешь поддержать меня? Станьте меценатом в https://patreon.com/jitterted
Оригинал: “https://dev.to/jitterted/live-coding-learnings-june-21-2019-1ihd”