Знакомство с Kotlin. Работа с шаблонизатором #
Васильев Андрей Михайлович, 2024
Версии презентации
Язык Kotlin #
- Разработан компанией JetBrains
- Представлен в 2011 году
- Версия 1.0 выпущена в 2016 году
- С 2019 является основным языком разработки под Android
- Версия 1.6 вышла в ноябре 2021 года
- Версия 1.7 вышла в июне 2022 года
- Версия 1.8 вышла в декабре 2022 года
- Версия 1.9 вышла в июле 2023 года
Примерно каждые полгода выходит новая версия. Тематика последних выпусков — расширение списка поддерживаемых платформ и переход на унифицированный компилятор
Особенности платформы #
- Можно разрабатывать приложения под несколько платформ:
- Мобильные приложения Android
- Настольные приложения без JVM, платформенный бинарный код
- Приложения под iOS
- Серверные веб-приложения
- Клиентские веб-приложения (платформа JavaScript)
- Клиентские веб-приложения (платформа WebAssembly)
- Можно разрабатывать библиотеки для различных платформ
- Можно использовать общую кодовую базу при разработке приложений под разные платформы
Мы будем рассматривать классический вариант — разработку приложений под JVM
Особенности языка Kotlin #
- Язык общего назначения, т.е. можно разрабатывать любые приложения
- Поддерживает современные парадигмы программирования:
- Императивная, процедурная
- Объектно-ориентированная
- Функциональная
- Обладает современным компактным синтаксисом
- Изначально нацелен на решение практических ограничений языка Java 7/8 (разработчикам JetBrains надоело писать слишком много кода)
- Динамично развивается, разработчики оставляют право за собой изменять детали работы языка
Изучение языка Kotlin #
- Официальный сайт языка
- Портал с документацией
- Руководство по языку Kotlin, русскоязычный перевод документации
- Упражнения для закрепления основ языка Kotlin Koans
- Дополнение EduTools для IDEA с задачами для изучения Kotlin
- Книга Kotlin. Программирование для профессионалов
Ментальная модель при изучении языка #
flowchart LR kotlin["Исходный код на Kotlin"] java["Исходный код на Java"] kotlin-jvm("Компилятор Kotlin\nдля JVM") javac("Компилятор Javac") kotlin-class[".class-файлы"] java-class[".class-файлы"] jvm("Виртуальная машина\nJVM") kotlin --> kotlin-jvm --> kotlin-class --> jvm java --> javac --> java-class --> jvm
- В качестве целевой платформы будем использовать только JVM
- Можно легко перенести идиоматические для Java решения:
- Деление кода на пакеты
- Написание классов
- Создание объектов из классов
- Использование коллекций (только стоит использовать классы Kotlin:
List
,Map
) - Использование перечислений
- Другие инструменты хорошо дополняют известные элементы и решают проблемы многословности Java-кода
- Разбираться с поддержкой других платформ в рамках курса не надо
Как запускать приложения #
- https://play.kotlinlang.org - ресурс для запуска простых приложений на Kotlin
- JetBrains IDEA
- Среда разработки Eclipse с плагином
- Редактор Visual Studio Code с плагином Kotlin.
- Любой текстовый редактор + система сборки
Минимальное приложение #
За исключением конфигурации системы сборки минимальное приложение состоит только лишь из функции:
fun main() {
println("Я работаю!")
}
- Синтаксис языка Си-подобный, т.е. уже знакомый и привычный
- Для описания функций и методов используется ключевое слово
fun
- Аргументы описываются в скобках после имени функции
- Для вызова функции используется знакомый синтаксис: название функции, за которым в круглых скобках указывается набор аргументов
Базовый синтаксис #
Рассмотрим основы синтаксиса языка Kotlin
- В целом всё знакомо
- Ключевые компоненты синтаксиса взяты из Си
- Описание типов переменных взято из Оберона (Паскаля, Модулы)
- Есть возможность писать функции в пакете, без привязки к классам
- Фукнции поддерживают значения аргументов по умолчанию
Разработка простого приложения #
Реализуем небольшое приложение, которое позволит:
- Считать последовательность чисел со стандартного потока ввода
- Найти минимальный элемент
- Найти максимальный элемент
- Подсчитать среднее значение последовательности
Функции в Kotlin #
Рассмотрим основы работы с функциями в Kotlin
- Функции являются объектами первого рода, т.е. ссылки на них могут быть помещены в переменные
- Подход к реализации функционального программирования повторяет решение из Java: функция является объектом класса, реализующего функцию интерфейса
- Удобно создавать лямбда-функции
- Удобно связывать лямбда-функции с вызывающими их методами
Null-безопасность #
Рассмотрим вопрос null-безопасности в Kotlin
- Отсутствие данных, null-ссылка, является краевым состоянием ссылки
- Данное состояние необходимо специально обрабатывать в любых языках
- Kotlin предоставляет удобные средства для обработки данной ситуации
Формирование HTML-документов #
- Шаблонизатор — это компонент, который формирует текстовый документ на основании шаблона и данных для отображения этого шаблона
- Шаблон — это размеченный специальным образом текст, в котором указаны места вставки данных
- Шаблон может быть как одним файлом, так и составлен из нескольких частей
Принципиальная схема работы шаблонизатора
flowchart LR renderer(Шаблонизатор) template[Шаблон] base_template[Базовый шаблон] partial[Частичный шаблон] data[Данные\nдля отображения] text[Текст] base_template --> template partial --> template template --> renderer data --> renderer renderer --> text
Шаблоном может выступать код, написанный на языке Kotlin, но лучше использовать специализированные языки для данной предметной области
Шаблонизатор Pebble #
Pebble — шаблонизатор для JVM, синтаксически схожий с Twig и Jinja
- Поддерживает большое количество встроенных фильтров и тегов
- Поддерживает наследование и подключение шаблонов друг в друга
- Поддерживает расширения: собственные теги, фильтры и функции
Использование Pebble #
Для использования шаблонизатора необходимо:
- Подключить соответствующую библиотеку к проекту в конфигурацию Gradle
- Создать объект шаблонизатора
- Для каждой отдельной страницы
- Создать класс, описывающий данные для отображения
- Создать файл шаблона, преобразующий данные в текст
Подключение к проекту #
Для подключения библиотеки Pebbletemplates необходимо в файле build.gradle
в разделе dependencies
добавить следующую строку:
implementation 'io.pebbletemplates:pebble:3.2.2'
При добавлении библиотеки старайтесь всегда выбирать актуальные выпуски, не содержащие критических уязвимостей. Для поиска последней версии можно воспользоваться, напирмер mvnrepository.com
Установка расширения для IDEA #
Изначально IDEA не поддерживает редактирование Pebble, однако существует расширение
- Поддерживающее подсветку синтаксиса языка Pebble
- Может быть настроено на подсветку целевого языка шаблона, например HTML
Создание шаблонов #
- Шаблоны обычно представляют собой файлы с расширением
.peb
, которые находятся в каталоге с ресурсами приложения - В Gradle ресурсы располагаются в каталоге
src/main/resources
Создадим шаблон-раскладку, которая будет являться базой для всех других страниц и назовём её layout.peb
:
<html>
<head>
<title>{% block title %}Сайт{% endblock %}</title>
</head>
<body>
<div id="content">
{% block content %}{% endblock %}
</div>
<div id="footer">
{% block footer %}
Copyright 2000-3088
{% endblock %}
</div>
</body>
</html>
Создадим шаблон для стартовой страницы и назовём его home.peb
:
{% extends "layout.peb" %}
{% block title %} Стартовая страница {% endblock %}
{% block content %}
<h1>Стартовая страница</h1>
<p>С очень важным содержимым</p>
<p>Время создания {{ time | date("yyyy-MM-dd HH:mm") }}</p>
{% endblock %}
{% %}
определяет места вызова управляющих конструкций Pebble{{ }}
определяет места для вывода результатов выражения в строкуblock
определяет места в раскладке для расширенияextends
позволяет наследовать один шаблон от другого- Фильтр
date
позволяет преобразовывать даты в формат, удобный для time
— данные для отображения на странице
Отображение шаблона #
- Необходимо создать объект шаблонизатора, который будет преобразовывать шаблоны в строки. Достаточно создать его 1 раз для всего приложения
- Необходимо скомпилировать целевой шаблон
- Сформировать набор данных для отображения
- Преобразовать данные в строку с помощью скомпилированного шаблона
val engine = PebbleEngine.Builder().build() // Создаём шаблонизатор
val template = engine.getTemplate("home.peb") // Компилируем шаблон home.peb
val data = mapOf("time" to LocalDateTime.now()) // Формируем данные
val writer = StringWriter()
template.evaluate(writer, data) // Записать сформированную шаблоном
println(writer)
По умолчанию если шаблону не передать данные, то он просто проигнорирует их отсутствие
Наполнение Pebble-шаблона #
<html>
<head>
<title>Пример шаблона</title>
</head>
<body>
<p>Имя: {{ model.name }}</p>
<p>День рождения: {{ model.birthDate | date("yyyy-MM-dd") }}
</body>
</html>
- Возможен вызов любых методов данных, которые были переданы
- Шаблонизатор предоставляет множество фильтров, которые можно применить к данным
{{ SOURCE | FILTER-1 | FILTER-2 }}
Доступ к переменным #
При обращении к полю переменной model.data
внутри шаблона Pebble попытается у переданного объекта вызвать следующие методы:
- Если
model
является словарём (наследникомMap
), то будет вызван методmodel.get("data")
model.getData()
model.isData()
model.hasData()
model.data()
model.data
Если в переменной list
содержится список, то к его полям можно обращаться с помощью list[0]
вместо list.get(0)
Если возвращённое значение на каком-то этапе будет null
, то шаблонизатор вернёт пустую строку
Передача списков данных в шаблоны #
Шаблонизаторы поддерживает работу и со списками данных
class Event(val start: LocalDateTime, val description: String)
class EventsList(val events: List<Event>)
Шаблон для отображения списка событий:
{% for event in model.events %} // model содержит ссылку на объект EventsList
<h3>Событие {{ event.description }}</h3>
<p>Начало события: {{ event.start | date("yyyy-MM-dd HH:mm",
timeZone="UTC") }}</p>
{% else %}
<p>События не описаны</p>
{% endfor %}
Управляющие конструкции, вывод которых не должен попасть в текст документа, помещаются внутри {% %}
Управляющие конструкции #
{% if category == "news" %}
{{ news }}
{% elseif category == "sports" %}
{{ sports }}
{% else %}
<p>Пожалуйста укажите категорию</p>
{% endif %}
- С помощью условных конструкций необходимо адаптировать данные для отображения
- Запрещено помещать логику по обработке, группировке, сортировке и т.п. внутри шаблона. Все эти действия необходимо произвести внутри основной логики приложения, а в шаблон необходимо передать уже подготовленные данные
- Условные выражения поддерживают логическое объединение выражений:
and
— логическое Иor
— логическое ИЛИnot
— логическое отрицание()
— группировка
Проверки #
В рамках условных выражений Pebble позволяет вызывать операцию is
и связывать её с проверками:
{% if 3 is odd %}
3 является нечётным числом
{% endif %}
При использовании оператора можно проверить отрицание проверки:
{% if name is not null %}
Имя не указано!
{% endif %}
Наследование шаблонов #
Шаблонизатор Pebble поддерживает концепцию иерархических шаблонов
- Базовый шаблон включает в себя основное содержимое и точки расширения
- Шаблон-расширение указывает содержимое для точек расширения
Базовый шаблон, например layout.peb
:
<html>
<body>
<div id="content">
{% block content %} {% endblock %}
</div>
<div id="footer">
{% block footer %} Default content {% endblock %}
</div>
</body>
</html>
Шаблон-расширение:
{% extends "./layout.peb" %}
{% block content %} Содержимое {% endblock %}
Включение шаблонов #
Помимо иерархического разделения Pebble поддерживает включение одних шаблонов в другие
- Шаблоны с описанием макросов
- Шаблоны для описания типовых компонентов
- Подменяемые статические конструкции страниц
<div class="sidebar">
{% include "advertisement.html" %}
</div>